From 19ebd9616df134dc75772f98efc5b0f05f5f6a45 Mon Sep 17 00:00:00 2001 From: zhouzihanntu Date: Fri, 24 Mar 2017 21:32:28 +0800 Subject: [PATCH 001/638] update --- ...odules-in-node-js-everything-you-need-to-know.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/TODO/requiring-modules-in-node-js-everything-you-need-to-know.md b/TODO/requiring-modules-in-node-js-everything-you-need-to-know.md index fce6fbc0359..5618ebc6fd9 100644 --- a/TODO/requiring-modules-in-node-js-everything-you-need-to-know.md +++ b/TODO/requiring-modules-in-node-js-everything-you-need-to-know.md @@ -1,23 +1,30 @@ > * 原文地址:[Requiring modules in Node.js: Everything you need to know](https://medium.freecodecamp.com/requiring-modules-in-node-js-everything-you-need-to-know-e7fbd119be8#.wcrwm9c81) > * 原文作者:[Samer Buna](https://medium.freecodecamp.com/@samerbuna?source=post_header_lockup) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 译者: +> * 译者:[zhouzihanntu](https://github.com/zhouzihanntu) > * 校对者: # Requiring modules in Node.js: Everything you need to know # +# 在 Node.js 中加载模块 # ## How modularity works in Node.js ## +## 模块化在 Node.js 中的应用## Node uses two core modules for managing module dependencies: +Node 提供了两个核心模块来管理模块依赖: - The `require` module, which appears to be available on the global scope — no need to `require('require')`. +- `require` 模块在全局范围内可用,不需要 `require('require')`. - The `module` module, which also appears to be available on the global scope — no need to `require('module')`. +- `module` 模块同样在全局范围内可用,不需要 `require('module')`. You can think of the `require` module as the command and the `module` module as the organizer of all required modules. +你可以把 `require` 模块当做命令 `module` 模块 所有需加载模块的 Requiring a module in Node isn’t that complicated of a concept. +在 Node 中加载一个模块其实没有概念那么复杂。 ``` const config = require('/path/to/file'); @@ -108,7 +115,7 @@ Error: Cannot find module 'find-me' If you now create a local `node_modules` directory and put a `find-me.js` in there, the `require('find-me')` line will find it. ``` -~/learn-node $ mkdir node_modules +~/learn-node $ mkdir node_modules ~/learn-node $ echo "console.log('I am not lost');" > node_modules/find-me.js @@ -302,7 +309,7 @@ In util Module { I’ve removed some attributes in the above output to keep it brief, but note how the `exports` object now has the attributes we defined in each module. You can put as many attributes as you want on that exports object, and you can actually change the whole object to be something else. For example, to change the exports object to be a function instead of an object, we do the following: ``` -// Add the following line in index.js before the console.log +// Add the following line in index.js before the console.log module.exports = function() {}; ``` From 312d6c3e62c36f666027f9008fe1081f8a5e4cea Mon Sep 17 00:00:00 2001 From: xiaoyusilen Date: Mon, 27 Mar 2017 17:37:50 +0800 Subject: [PATCH 002/638] complete translation --- TODO/go-function-calls-redux.md | 56 ++++++++++++++++----------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/TODO/go-function-calls-redux.md b/TODO/go-function-calls-redux.md index 3ee8548a7b9..2e8b6efa515 100644 --- a/TODO/go-function-calls-redux.md +++ b/TODO/go-function-calls-redux.md @@ -1,18 +1,18 @@ > * 原文地址:[Go Function Calls Redux](https://hackernoon.com/go-function-calls-redux-609fdd1c90fd#.jsh5r78wp) > * 原文作者:[Phil Pearl](https://hackernoon.com/@philpearl?source=post_header_lockup) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 译者: +> * 译者:[xiaoyusilen](http://xiaoyu.world) > * 校对者: -# Go Function Calls Redux # +# Go 函数调用 Redux # -Some time ago in a [previous post](https://syslog.ravelin.com/anatomy-of-a-function-call-in-go-f6fc81b80ecc#.gpqsgzmjc) I promised to take a further look at how function calls and call stacks in Go work. I’ve think found a neat way to make good on that promise, so here goes. +前段时间在一篇[文章](https://syslog.ravelin.com/anatomy-of-a-function-call-in-go-f6fc81b80ecc#.gpqsgzmjc)中我答应写一篇进一步分析 Go 中如何进行函数调用和调用堆栈在 Go 中如何工作的文章。现在我找到了一种简洁的方式来向大家展示上述内容,所以有了现在这篇文章。 -So what is a call stack? Well, it’s an area of memory used to hold local variables and call parameters, and to track where functions should return to. Each goroutine has it’s own stack. You could almost say a goroutine is its stack. +什么是调用堆栈?它是一个用于保存局部变量和调用参数的内存区域,并且跟踪每个函数应该返回到哪里去。每个 goroutine 都有它自己的堆栈。你甚至可以说每个 goroutine 就是它自己的堆栈。 -Here’s the code I’m going to use to show the stack in action. It’s just a sequence of simple function calls. main() calls [f1(0xdeadbeef)](https://en.wikipedia.org/wiki/Hexspeak) , which then calls `f2(0xabad1dea)`, which calls `f3(0xbaddcafe)`. `f3()` then adds one to it’s parameter, and stores it in a local variable called `local`. It then takes the address of `local` and prints out memory starting at that address. Because `local` is on the stack, this prints the stack. +下面是我用于演示堆栈的代码。就是一系列简单的函数调用,main() 函数调用 [f1(0xdeadbeef)](https://en.wikipedia.org/wiki/Hexspeak),然后调用 `f2(0xabad1dea)`,再调用 `f3(0xbaddcafe)`。然后 f3() 将其中一个作为它的参数,并且将它存储在名为 `local` 的本地变量中。然后获取 `local` 的内存地址并且从那里开始输出。因为 `loacl` 在栈内,所以输出的就是栈。 -``` +```go package main import ( @@ -59,9 +59,9 @@ func showFunc(at uintptr) { } ``` -Here’s the output of the program. It is a dump of memory starting at the address of `local`, shown as a list of 8-byte integers in hex. The address of each integer is on the left, and the int at the address is on the right. +下面是上述代码的输出结果。它是从 `local` 的地址开始的内存转储,是以十六进制形式展示的8字节列表。左边是每个整数的存储地址,右边是地址内存储的整数。 -We know `local` should equal 0xBADDCAFE + 1, or 0xBADDCAFF, and this is indeed what we see at the start of the dump. +我们知道 `local` 应该等于 0xBADDCAFE + 1,或者 0xBADDCAFF,这确实是我们转储开始时看到的。 ``` C42003FF28: BADDCAFF @@ -94,13 +94,13 @@ C42003FFC0: C4200001A0 102752A is runtime.main /usr/local/Cellar/go/1.8/libexec/src/runtime/proc.go 194 ``` -- The next number is 0xC42003FF48, which is the address of the 5th line of the dump. -- After that we have 0x1088BEB. It turns out this is an address of executable code, and if we feed it into `runtime.FuncForPC` we see it is the address of line 19 of main.go, which is the last line of `f2()`. This is the address we return to when `f3()` returns. -- Next we have 0xBADDCAFE, the parameter to our call to `f3()` +- 下一个数字是 0xC42003FF48,它是转储的第五行的地址。 +- 然后我们可以得到 0x1088BEB。事实上这是一个可执行代码的地址,如果我们将它作为 `runtime.FuncForPC` 的参数,我们知道它是 main.go 的第19行代码的地址,也是 f2() 的最后一行代码。这是 f3() 返回时我们得到的地址。 +- 接下来我们得到 0xBADDCAFE,这是我们调用 `f3()` 时的参数。 -If we carry on we continue to see this pattern. Below I’ve marked up the memory dump showing how the stack pointers track back through the dump and where the function parameters and return addresses sit. +如果继续我们将看到类似上面的输出结果。下面我已经标记了内存转储,显示堆栈指针如何跟踪转储,参数和返回地址在哪里。 -``` +```go C42003FF28: BADDCAFF Local variable in f3() +-C42003FF30: C42003FF48 | C42003FF38: 1088BEB return to f2() main.go line 19 @@ -113,18 +113,18 @@ If we carry on we continue to see this pattern. Below I’ve marked up the memor | C42003FF70: DEADBEEF f1() parameter +-C42003FF78: C42003FFD0 C42003FF80: 102752A return to runtime.main() - ``` -From this we can see many things. +通过这些我们可以看出: -- First, the stack starts at a high address, and the stack address reduces as function calls are made. -- When a function call is made, the caller pushes the parameters onto the stack, then the return address (the address of the next instruction in the calling function), then a pointer to a higher point in the stack. -- This pointer is used to find the previous function call on the stack when unwinding the stack when the call returns. -- Local variables are stored after the stack pointer. +- 首先,堆栈从高地址开始,堆栈地址随着函数调用变小。 +- 当进行函数调用时,调用者将参数放入栈内,然后返回地址(调用函数中的下一条指令的地址),接着指向堆栈中较高的指针。 +- 当调用返回时,这个指针用于在堆栈中查找先前调用的函数。 +- 局部变量存储在堆栈指针之后。 -We can use the same technique to look at some slightly more complicated function calls. I’ve added more parameters, and some return values to `f2()` in this version. -``` +我们可以使用相同的技巧来分析一些稍微复杂的函数调用。这次,我添加了更多的参数,`f2()` 函数也返回了更多的值。 + +```go package main import ( @@ -153,9 +153,9 @@ func f3(val int) { } ``` -This time I’ve jumped straight to the marked-up output. +这次我们直接看被我标记好的输出结果。 -``` +```go C42003FF10: BADDCAFF local variable in f3() +-C42003FF18: C42003FF30 | C42003FF20: 1088BFB return to f2() @@ -173,11 +173,11 @@ This time I’ve jumped straight to the marked-up output. C42003FF80: 102752A return to runtime.main() ``` -From this we can see that +从结果中我们可以看出: -- the calling function makes space for the return values of the called function before the function parameters. (Note the values are uninitialised because the function hasn’t returned yet!) -- parameters are pushed onto the stack in reverse order. +- 调用函数在函数参数之前为被调用函数的返回值提供空间。(注意这些值是没有初始化的,因为这个函数还没有返回!) +- 参数在栈内的顺序与入栈顺序相反。 -Hopefully all that made sense! If you got this far and enjoyed it or learned something, please hit that heart-button. I can’t earn internet points without you. +希望我都讲清楚了。既然你已经看到这儿了,如果喜欢我的这篇文章或者可以从中学到一点什么的话,那么请给我点个赞。不然我就没办法获得积分。 -*By day, Phil fights crime at* [*ravelin.com*](https://ravelin.com) *. You can join him:* [*https://angel.co/ravelin/jobs*](https://angel.co/ravelin/jobs) . +**Phil 白天在 [ravelin.com](https://ravelin.com) 的工作主要是防止网上欺诈,你可以加入他 https://angel.co/ravelin/jobs。** \ No newline at end of file From 4c3b930b904ff06b37997f0fa0ea873bf11b5fd1 Mon Sep 17 00:00:00 2001 From: zhouzihanntu Date: Tue, 28 Mar 2017 19:05:01 +0800 Subject: [PATCH 003/638] update file --- ...-in-node-js-everything-you-need-to-know.md | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/TODO/requiring-modules-in-node-js-everything-you-need-to-know.md b/TODO/requiring-modules-in-node-js-everything-you-need-to-know.md index 5618ebc6fd9..fef57b8f8e0 100644 --- a/TODO/requiring-modules-in-node-js-everything-you-need-to-know.md +++ b/TODO/requiring-modules-in-node-js-everything-you-need-to-know.md @@ -16,41 +16,52 @@ Node uses two core modules for managing module dependencies: Node 提供了两个核心模块来管理模块依赖: - The `require` module, which appears to be available on the global scope — no need to `require('require')`. -- `require` 模块在全局范围内可用,不需要 `require('require')`. +- `require` 模块在全局范围内可用,不需要 `require('require')`. - The `module` module, which also appears to be available on the global scope — no need to `require('module')`. - `module` 模块同样在全局范围内可用,不需要 `require('module')`. You can think of the `require` module as the command and the `module` module as the organizer of all required modules. -你可以把 `require` 模块当做命令 `module` 模块 所有需加载模块的 +你可以将 `require` 模块理解为命令,将 `module` 模块理解为组织所有需加载模块的工具。 Requiring a module in Node isn’t that complicated of a concept. -在 Node 中加载一个模块其实没有概念那么复杂。 +在 Node 中加载一个模块其实不像概念那么复杂。 ``` const config = require('/path/to/file'); ``` The main object exported by the `require` module is a function (as used in the above example). When Node invokes that `require()` function with a local file path as the function’s only argument, Node goes through the following sequence of steps: +`require` 模块导出的主对象是一个函数(如上例)。当 Node 将本地文件路径作为唯一参数调用 `require()` 时,Node 将执行以下步骤: - **Resolving**: To find the absolute path of the file. +- **解析**:找到该文件的绝对路径。 - **Loading**: To determine the type of the file content. +- **加载**:确定文件内容的类型。 - **Wrapping**: To give the file its private scope. This is what makes both the `require` and `module` objects local to every file we require. +- **打包**:为文件划分私有作用域,这样 `require` 和 `module` 两个对象对于我们要加载的每个模块来说就都是本地的。 - **Evaluating**: This is what the VM eventually does with the loaded code. +- **评估**:最后由虚拟机对加载得到的代码做评估。 - **Caching**: So that when we require this file again, we don’t go over all the steps another time. +- **缓存**:当再次加载该文件时,无需再重复以上所有步骤。 In this article, I’ll attempt to explain with examples these different stages and how the affect the way we write modules in Node. +在本文中,我将试着用示例说明这些不同阶段,以及它们如何影响我们在 Node 中编写模块的方式。 Let me first create a directory to host all the examples using my terminal: +我先使用终端创建一个目录来托管本文中的所有示例: ``` mkdir ~/learn-node && cd ~/learn-node ``` All the commands in the rest of this article will be run from within `~/learn-node`. +本文余下部分的所有命令都将在 `~/learn-node` 目录下运行。 #### Resolving a local path #### +#### 解析本地路径 #### Let me introduce you to the `module` object. You can check it out in a simple REPL session: +首先,让我来介绍一下 `module` 对象。你可以通过一个简单的 REPL 会话查看它: ``` ~/learn-node $ node @@ -62,10 +73,12 @@ Module { filename: null, loaded: false, children: [], - paths: [ ... ] } + paths: [ ... ] +} ``` Every module object gets an `id` property to identify it. This `id` is usually the full path to the file, but in a REPL session it’s simply `.` +每个模块对象都有一个用于识别该对象的 `id` 属性。这个 `id` 通常是该文件的完整路径,但在 REPL 会话中 `.` Node modules have a one-to-one relation with files on the file-system. We require a module by loading the content of a file into memory. From e5e391827b3716c10ba3d4e3ec1d339747604168 Mon Sep 17 00:00:00 2001 From: tanglie Date: Wed, 29 Mar 2017 00:40:19 +0800 Subject: [PATCH 004/638] Update Dependency-Injection-with-Dagger-2.md --- TODO/Dependency-Injection-with-Dagger-2.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/TODO/Dependency-Injection-with-Dagger-2.md b/TODO/Dependency-Injection-with-Dagger-2.md index 803b9ce7a84..453d660e042 100644 --- a/TODO/Dependency-Injection-with-Dagger-2.md +++ b/TODO/Dependency-Injection-with-Dagger-2.md @@ -4,21 +4,21 @@ > * 译者: > * 校对者: -# Dependency Injection with Dagger 2 +# 用 Dagger 2 实现依赖注入 -## Overview +## 概要 -Many Android apps rely on instantiating objects that often require other dependencies. For instance, a Twitter API client may be built using a networking library such as [[Retrofit|Consuming-APIs-with-Retrofit]]. To use this library, you might also need to add parsing libraries such as [[Gson|Leveraging-the-Gson-Library]]. In addition, classes that implement authentication or caching may require accessing [[shared preferences|Storing-and-Accessing-SharedPreferences]] or other common storage, requiring instantiating them first and creating an inherent dependency chain. +很多 Android 应用依赖于一些含有其它依赖的对象。例如,一个 Twitter API 客户端可能需要通过 [Retrofit](https://github.com/codepath/android_guides/wiki/Consuming-APIs-with-Retrofit) 之类的网络库被构建。要使用这个库,你可能还需要添加 [Gson](https://github.com/codepath/android_guides/wiki/Leveraging-the-Gson-Library) 这样的解析库。另外,实现认证或缓存的库可能需要使用 [shared preferences](https://github.com/codepath/android_guides/wiki/Storing-and-Accessing-SharedPreferences)或其它通用存储方式。这就需要先把它们实例化,并创建一个隐含的依赖链。 -If you're not familiar with Dependency Injection, watch [this](https://www.youtube.com/watch?v=IKD2-MAkXyQ) quick video. +如果你不熟悉依赖注入,看看[这个](https://www.youtube.com/watch?v=IKD2-MAkXyQ) 短视频。 -Dagger 2 analyzes these dependencies for you and generates code to help wire them together. While there are other Java dependency injection frameworks, many of them suffered limitations in relying on XML, required validating dependency issues at run-time, or incurred performance penalties during startup. [Dagger 2](http://google.github.io/dagger/) relies purely on using Java [annotation processors](https://www.youtube.com/watch?v=dOcs-NKK-RA) and compile-time checks to analyze and verify dependencies. It is considered to be one of the most efficient dependency injection frameworks built to date. +Dagger 2 为你解析这些依赖,并生成把它们绑定在一起的代码。也有很多其它的 Java 依赖注入框架,但它们中很多个是有缺陷的,比如依赖 XML,需要在运行时验证依赖,或者在起始时造成性能负担。 [Dagger 2](http://google.github.io/dagger/) 纯粹依赖于 Java [annotation processors](https://www.youtube.com/watch?v=dOcs-NKK-RA) 以及编译时检查来分析并验证依赖。它被认为是目前最高效的依赖注入框架之一。 -### Advantages +### 优点 -Here is a list of other advantages for using Dagger 2: +这是使用 Dagger 2 的一系列其它优势: - * **Simplifies access to shared instances**. Just as the [[ButterKnife|Reducing-View-Boilerplate-with-Butterknife]] library makes it easier to define references to Views, event handlers, and resources, Dagger 2 provides a simple way to obtain references to shared instances. For instance, once we declare in Dagger our singleton instances such as `MyTwitterApiClient` or `SharedPreferences`, we can declare fields with a simple `@Inject` annotation: + * **简化共享实例访问**。就像 [ButterKnife](https://github.com/codepath/android_guides/wiki/Reducing-View-Boilerplate-with-Butterknife) 库简化了引用View, event handler 和 resources 的方式一样,Dagger 2 提供了一个简单的方式获取对共享对象的引用。例如,一旦我们在 Dagger 中声明了  `MyTwitterApiClient` 或 `SharedPreferences` 的单例,就可以用一个简单的 `@Inject` 标注来声明域: ```java public class MainActivity extends Activity { @@ -31,11 +31,11 @@ public class MainActivity extends Activity { } ``` - * **Easy configuration of complex dependencies**. There is an implicit order in which your objects are often created. Dagger 2 walks through the dependency graph and [[generates code|Dependency-Injection-with-Dagger-2#code-generation]] that is both easy to understand and trace, while also saving you from writing the large amount of boilerplate code you would normally need to write by hand to obtain references and pass them to other objects as dependencies. It also helps simplify refactoring, since you can focus on what modules to build rather than focusing on the order in which they need to be created. + * **容易配置复杂的依赖**。 对象创建是有隐含顺序的。Dagger 2 浏览依赖图,并且[生成易于理解和追踪的代码](https://github.com/codepath/android_guides/wiki/Dependency-Injection-with-Dagger-2#code-generation)。而且,它可以节约大量的样板代码,使你不再需要手写,手动获取引用并把它们传递给其他对象作为依赖。它也简化了重构,因为你可以聚焦于构建模块本身,而不是它们被创建的顺序。 - * **Easier unit and integration testing** Because the dependency graph is created for us, we can easily swap out modules that make network responses and mock out this behavior. + * **更简单的单元和集成测试** 因为依赖图是为我们创建的,我们可以轻易换出用于创建网络响应的模块,并模拟这种行为。 - * **Scoped instances** Not only can you easily manage instances that can last the entire application lifecycle, you can also leverage Dagger 2 to define instances with shorter lifetimes (i.e. bound to a user session, activity lifecycle, etc.). + * **实例范围** 你不仅可以轻易地管理持续整个应用生命周期的实例,也可以利用 Dagger 2 来定义生命周期更短(比如和一个用户 session 或 Activity 生命周期相绑定)的实例。 ### Setup From 22d75b762801bf962d8a5864d46e6ffb96f856de Mon Sep 17 00:00:00 2001 From: tanglie Date: Wed, 29 Mar 2017 01:00:13 +0800 Subject: [PATCH 005/638] Update Dependency-Injection-with-Dagger-2.md --- TODO/Dependency-Injection-with-Dagger-2.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/TODO/Dependency-Injection-with-Dagger-2.md b/TODO/Dependency-Injection-with-Dagger-2.md index 453d660e042..199a78c5438 100644 --- a/TODO/Dependency-Injection-with-Dagger-2.md +++ b/TODO/Dependency-Injection-with-Dagger-2.md @@ -37,11 +37,11 @@ public class MainActivity extends Activity { * **实例范围** 你不仅可以轻易地管理持续整个应用生命周期的实例,也可以利用 Dagger 2 来定义生命周期更短(比如和一个用户 session 或 Activity 生命周期相绑定)的实例。 -### Setup +### 设置 -Android Studio by default will not allow you to navigate to generated Dagger 2 code as legitimate classes because they are not normally added to the source path but adding the `android-apt` plugin will add these files into the IDE classpath and enable you to have more visibility. +默认的 Android Studio 不把生成的 Dagger 2 代码视作合法的类,因为它们通常并不被加入 source 路径。但引入 `android-apt` 插件后,它会把这些文件加入 IDE classpath,从而提供更好的可见性。 -Make sure to [[upgrade|Getting-Started-with-Gradle#upgrading-gradle]] to the latest Gradle version to use the `annotationProcessor` syntax: +确保[升级](https://github.com/codepath/android_guides/wiki/Getting-Started-with-Gradle#upgrading-gradle) 到最迟的 Gradle 版本以使用最新的 `annotationProcessor` 语法: ```gradle dependencies { @@ -52,12 +52,12 @@ dependencies { } ``` -Note that the `provided` keyword refers to dependencies that are only needed at compilation. The Dagger compiler generates code that is used to create the dependency graph of the classes defined in your source code. These classes are added to the IDE class path during compilation. The `annotationProcessor` keyword, which is understood by the Android Gradle plugin, does not add these classes to the class path, they are used only for annotation processing, which prevents accidentally referencing them. +注意 `provided` 关键词是指只在编译时需要的依赖。Dagger 编译器生成了用于生成依赖图的类,而这个依赖图是在你的源代码中定义的。这些类在编译过程中被添加到你的IDE classpath。`annotationProcessor` 关键字可以被 Android Gradle 插件理解。它不把这些类添加到 classpath 中,而只是把它们用于处理注解。这可以避免不小心引用它们。 -### Creating Singletons -![Dagger Injections Overview](https://raw.githubusercontent.com/codepath/android_guides/master/images/dagger_general.png) +### 创建单例 +![Dagger 注入概要](https://raw.githubusercontent.com/codepath/android_guides/master/images/dagger_general.png) -The simplest example is to show how to centralize all your singleton creation with Dagger 2. Suppose you weren't using any type of dependency injection framework and wrote code in your Twitter client similar to the following: +最简单的例子是用 Dagger 2 集中管理所有的单例。假设你不用任何依赖注入框架,在你的 Twitter 客户端中写下类似这些的东西: ```java OkHttpClient client = new OkHttpClient(); @@ -82,9 +82,9 @@ Retrofit retrofit = new Retrofit.Builder() .build(); ``` -#### Declare your singletons +#### 声明你的单例 -You need to define what objects should be included as part of the dependency chain by creating a Dagger 2 **module**. For instance, if we wish to make a single `Retrofit` instance tied to the application lifecycle and available to all our activities and fragments, we first need to make Dagger aware that a `Retrofit` instance can be provided. +你需要通过创建 Dagger 2 **模块**定义哪些对象应该作为依赖链的一部分。例如,假设我们想要创建一个 `Retrofit` 单例,使它绑定到应用生命周期,对所有的 Activity 和 Fragment 都可用,我们首先需要使 Dagger 意识到他可以提供 `Retrofit` 的实例。 Because we wish to setup caching, we need an Application context. Our first Dagger module, `AppModule.java`, will be used to provide this reference. We will define a method annotated with `@Provides` that denotes to Dagger that this method is the constructor for the `Application` return type: From fa775fb725f0617c96a42d856d69c9e754846c71 Mon Sep 17 00:00:00 2001 From: zhouzihanntu Date: Wed, 29 Mar 2017 19:14:06 +0800 Subject: [PATCH 006/638] update file --- ...-in-node-js-everything-you-need-to-know.md | 133 ++++++++---------- 1 file changed, 60 insertions(+), 73 deletions(-) diff --git a/TODO/requiring-modules-in-node-js-everything-you-need-to-know.md b/TODO/requiring-modules-in-node-js-everything-you-need-to-know.md index fef57b8f8e0..0f35c99a5d3 100644 --- a/TODO/requiring-modules-in-node-js-everything-you-need-to-know.md +++ b/TODO/requiring-modules-in-node-js-everything-you-need-to-know.md @@ -5,63 +5,46 @@ > * 校对者: # Requiring modules in Node.js: Everything you need to know # -# 在 Node.js 中加载模块 # +# 在 Node.js 中引用模块 # -## How modularity works in Node.js ## -## 模块化在 Node.js 中的应用## +## Node.js 中模块化的工作原理 ## -Node uses two core modules for managing module dependencies: Node 提供了两个核心模块来管理模块依赖: -- The `require` module, which appears to be available on the global scope — no need to `require('require')`. -- `require` 模块在全局范围内可用,不需要 `require('require')`. -- The `module` module, which also appears to be available on the global scope — no need to `require('module')`. -- `module` 模块同样在全局范围内可用,不需要 `require('module')`. +- `require` 模块在全局范围内可用,不需要写 `require('require')`. +- `module` 模块同样在全局范围内可用,不需要写 `require('module')`. -You can think of the `require` module as the command and the `module` module as the organizer of all required modules. -你可以将 `require` 模块理解为命令,将 `module` 模块理解为组织所有需加载模块的工具。 +你可以将 `require` 模块理解为命令,将 `module` 模块理解为组织所有被引用模块的工具。 -Requiring a module in Node isn’t that complicated of a concept. -在 Node 中加载一个模块其实不像概念那么复杂。 +在 Node 中引入一个模块其实不像概念那么复杂。 ``` const config = require('/path/to/file'); ``` -The main object exported by the `require` module is a function (as used in the above example). When Node invokes that `require()` function with a local file path as the function’s only argument, Node goes through the following sequence of steps: `require` 模块导出的主对象是一个函数(如上例)。当 Node 将本地文件路径作为唯一参数调用 `require()` 时,Node 将执行以下步骤: -- **Resolving**: To find the absolute path of the file. - **解析**:找到该文件的绝对路径。 -- **Loading**: To determine the type of the file content. - **加载**:确定文件内容的类型。 -- **Wrapping**: To give the file its private scope. This is what makes both the `require` and `module` objects local to every file we require. -- **打包**:为文件划分私有作用域,这样 `require` 和 `module` 两个对象对于我们要加载的每个模块来说就都是本地的。 -- **Evaluating**: This is what the VM eventually does with the loaded code. +- **打包**:为文件划分私有作用域,这样 `require` 和 `module` 两个对象对于我们要引入的每个模块来说就都是本地的。 - **评估**:最后由虚拟机对加载得到的代码做评估。 -- **Caching**: So that when we require this file again, we don’t go over all the steps another time. -- **缓存**:当再次加载该文件时,无需再重复以上所有步骤。 +- **缓存**:当再次引用该文件时,无需再重复以上所有步骤。 -In this article, I’ll attempt to explain with examples these different stages and how the affect the way we write modules in Node. -在本文中,我将试着用示例说明这些不同阶段,以及它们如何影响我们在 Node 中编写模块的方式。 +在本文中,我将试着用示例说明这些不同阶段的运行原理,以及它们如何影响我们在 Node 中编写模块的方式。 -Let me first create a directory to host all the examples using my terminal: 我先使用终端创建一个目录来托管本文中的所有示例: ``` mkdir ~/learn-node && cd ~/learn-node ``` -All the commands in the rest of this article will be run from within `~/learn-node`. 本文余下部分的所有命令都将在 `~/learn-node` 目录下运行。 -#### Resolving a local path #### #### 解析本地路径 #### -Let me introduce you to the `module` object. You can check it out in a simple REPL session: -首先,让我来介绍一下 `module` 对象。你可以通过一个简单的 REPL 会话查看它: +首先,让我来介绍一下 `module` 对象。你可以在一个简单的 REPL 会话中查看该对象: ``` ~/learn-node $ node @@ -77,20 +60,19 @@ Module { } ``` -Every module object gets an `id` property to identify it. This `id` is usually the full path to the file, but in a REPL session it’s simply `.` -每个模块对象都有一个用于识别该对象的 `id` 属性。这个 `id` 通常是该文件的完整路径,但在 REPL 会话中 `.` +每个模块对象都有一个用于识别该对象的 `id` 属性。这个 `id` 通常是该文件的完整路径,但在 REPL 会话中只会显示为 ``。 -Node modules have a one-to-one relation with files on the file-system. We require a module by loading the content of a file into memory. +Node 模块与文件系统中的文件有着一一对应的关系。我们通过加载模块对应的文件内容到内存中来实现模块引用。 -However, since Node allows many ways to require a file (for example, with a relative path or a pre-configured path), before we can load the content of a file into the memory we need to find the absolute location of that file. +然而,由于 Node 允许使用许多方式引入文件(例如,使用相对路径或预先配置的路径),我们需要在将文件的内容加载到内存前找到该文件的绝对位置。 -When we require a `'find-me'` module, without specifying a path: +例如,我们不声明路径,直接引入一个 `'find-me'` 模块时: ``` require('find-me'); ``` -Node will look for `find-me.js` in all the paths specified by `module.paths` — in order. +Node 会在 `module.paths` 声明的所有路径中依次查找 `find-me.js` 。 ``` ~/learn-node $ node @@ -105,9 +87,9 @@ Node will look for `find-me.js` in all the paths specified by `module.paths`  '/usr/local/Cellar/node/7.7.1/lib/node' ] ``` -The paths list is basically a list of node_modules directories under every directory from the current directory to the root directory. It also includes a few legacy directories whose use is not recommended. +Node 从当前目录开始一级级向上寻找 node_modules 目录,这个数组大致就是当前目录到所有 node_modules 目录的相对路径。其中还包括一些为了兼容性保留的目录,不推荐使用。 -If Node can’t find `find-me.js` in any of these paths, it will throw a “cannot find module error.” +如果 Node 在以上路径中都无法找到 `find-me.js` ,将抛出一个 “找不到该模块” 错误。 ``` ~/learn-node $ node @@ -125,7 +107,7 @@ Error: Cannot find module 'find-me' at REPLServer.onLine (repl.js:533:10) ``` -If you now create a local `node_modules` directory and put a `find-me.js` in there, the `require('find-me')` line will find it. +如果你现在创建一个本地的 `node_modules` 目录,并向目录中添加一个 `find-me.js` 文件,就能通过 `require('find-me')` 找到它了。 ``` ~/learn-node $ mkdir node_modules @@ -139,14 +121,14 @@ I am not lost > ``` -If another `find-me.js` file existed in any of the other paths, for example, if we have a `node_modules` directory under the home directory and we have a different `find-me.js` file in there: +如果在其他路径下也有 `find-me.js` 文件呢?例如,我们在主目录下的 `node_modules` 目录中放置一个不同的 `find-me.js` 文件: ``` $ mkdir ~/node_modules $ echo "console.log('I am the root of all problems');" > ~/node_modules/find-me.js ``` -When we `require('find-me')` from within the `learn-node` directory — which has its own `node_modules/find-me.js`, the `find-me.js` file under the home directory will not be loaded at all: +当我们在 `learn-node` 目录下执行 `require('find-me')` 时,`learn-node` 目录会加载自己的 `node_modules/find-me.js`,主目录下的 `find-me.js` 文件并不会被加载: ``` ~/learn-node $ node @@ -156,7 +138,7 @@ I am not lost > ``` -If we remove the local `node_modules` directory under `~/learn-node` and try to require `find-me` one more time, the file under the home’s `node_modules` directory would be used: +此时,如果我们将 `~/learn-node` 下的 `node_modules` 移除,再一次引入 `find-me` 模块,那么主目录下的 `node_modules` 将会被加载: ``` ~/learn-node $ rm -r node_modules/ @@ -168,9 +150,9 @@ I am the root of all problems > ``` -#### Requiring a folder #### +#### 引入文件夹 #### -Modules don’t have to be files. We can also create a `find-me` folder under `node_modules` and place an `index.js` file in there. The same `require('find-me')` line will use that folder’s `index.js` file: +模块不一定是单个文件。我们也可以在 `node_modules` 目录下创建一个 `find-me` 文件夹,然后向其中添加一个 `index.js` 文件。`require('find-me')` 会引用该文件夹下的 `index.js` 文件: ``` ~/learn-node $ mkdir -p node_modules/find-me @@ -184,9 +166,9 @@ Found again. > ``` -Note how it ignored the home directory’s `node_modules` path again since we have a local one now. +>注意,由于我们现在有一个本地目录,它再次忽略了主目录的 `node_modules` 路径。 -An `index.js` file will be used by default when we require a folder, but we can control what file name to start with under the folder using the `main` property in `package.json`. For example, to make the `require('find-me')` line resolve to a different file under the `find-me` folder, all we need to do is add a `package.json` file in there and specify which file should be used to resolve this folder: +当我们引入一个文件夹时,将默认执行 `index.js` 文件,但是我们可以通过 `package.json` 中的 `main` 属性指定主入口文件。例如,要令 `require('find-me')` 解析到 `find-me` 文件夹下的另一个文件,我们只需要在该文件夹下添加一个 `package.json` 文件来声明解析该文件夹时引用的文件: ``` ~/learn-node $ echo "console.log('I rule');" > node_modules/find-me/start.js @@ -200,9 +182,9 @@ I rule > ``` -#### require.resolve #### +#### require.resolve 方法 #### -If you want to only resolve the module and not execute it, you can use the `require.resolve` function. This behaves exactly the same as the main `require` function, but does not load the file. It will still throw an error if the file does not exist and it will return the full path to the file when found. +如果你只想解析模块而不运行,此时可以使用 `require.resolve` 函数。这个方法与 `require` 的主要功能完全相同,但是不加载文件。如果文件不存在,它仍会抛出错误,并在找到文件时返回文件的完整路径。 ``` > require.resolve('find-me'); @@ -222,34 +204,34 @@ Error: Cannot find module 'not-there' > ``` -This can be used, for example, to check whether an optional package is installed or not and only use it when it’s available. +这个方法可以用于检查一个可选安装包是否安装,并仅在该包可用时使用。 -#### Relative and absolute paths #### +#### 相对路径和绝对路径 #### -Besides resolving modules from within the `node_modules` directories, we can also place the module anywhere we want and require it with either relative paths (`./` and `../`) or with absolute paths starting with `/`. +除了从 `node_modules` 目录中解析模块以外,我们还可以将模块放置在任意位置,使用相对路径( `./` 和 `../` )或以 `/` 开头的绝对路径引入。 -If, for example, the `find-me.js` file was under a `lib` folder instead of the `node_modules` folder, we can require it with: +举个例子,如果 `find-me.js` 文件并不在 `node_modules` 中,而在 `lib` 文件夹中。我们可以使用以下代码引入它: ``` require('./lib/find-me'); ``` -#### Parent-child relation between files #### +#### 文件间的父子关系 #### -Create a `lib/util.js` file and add a `console.log` line there to identify it. Also, `console.log` the `module` object itself: +现在我们来创建一个 `lib/util.js` 文件,向文件添加一行 `console.log` 代码作为标识。打印出 `module` 对象本身: ``` ~/learn-node $ mkdir lib ~/learn-node $ echo "console.log('In util', module);" > lib/util.js ``` -Do the same for an `index.js` file, which is what we’ll be executing with the node command. Make this `index.js` file require `lib/util.js`: +同样的,向 `index.js` 文件中也添加一行打印 `module` 对象的代码,并在文件中引入 `lib/util.js`,我们将使用 node 命令运行该文件: ``` ~/learn-node $ echo "console.log('In index', module); require('./lib/util');" > index.js ``` -Now execute the `index.js` file with node: +用 node 运行 `index.js` 文件: ``` ~/learn-node $ node index.js @@ -279,25 +261,27 @@ In util Module { paths: [...] } ``` -Note how the main `index` module `(id: '.')` is now listed as the parent for the `lib/util` module. However, the `lib/util` module was not listed as a child of the `index` module. Instead, we have the `[Circular]` value there because this is a circular reference. If Node prints the `lib/util` module object, it will go into an infinite loop. That’s why it simply replaces the `lib/util` reference with `[Circular]`. +>注意:`index` 主模块 `(id: '.')` 现在被列为 `lib/util` 模块的父类。但 `lib/util` 模块并没有被列为 `index` 模块的子目录。相反,我们在这里得到的值是 `[Circular]`,因为这是一个循环引用。如果 Node 打印 `lib/util` 模块对象,将进入一个无限循环。这就是为什么 Node 使用 `[Circular]` 代替了 `lib/util` 引用。 -More importantly now, what happens if the `lib/util` module required the main `index` module? This is where we get into what’s known as the circular modular dependency, which is allowed in Node. + +重点来了,如果我们在 `lib/util` 模块中引入 `index` 主模块会发生什么?这就是 Node 中所支持的循环模块依赖关系。 To understand it better, let’s first understand a few other concepts on the module object. +为了更好理解循环模块依赖,我们先来了解一些关于 module 对象的概念。 -#### exports, module.exports, and synchronous loading of modules #### +#### exports、module.exports 和模块异步加载 #### -In any module, exports is a special object. If you’ve noticed above, every time we’ve printed a module object, it had an exports property which has been an empty object so far. We can add any attribute to this special exports object. For example, let’s export an id attribute for `index.js` and `lib/util.js`: +在所有模块中,exports 都是一个特殊对象。你可能注意到了,以上我们每打印一个 module 对象时,它都有一个空的 exports 属性。我们可以向这个特殊的 exports 对象添加任意属性。例如,我们现在为 `index.js` 和 `lib/util.js` 的 exports 对象添加一个 id 属性: ``` -// Add the following line at the top of lib/util.js +// 在 lib/util.js 顶部添加以下代码 exports.id = 'lib/util'; -// Add the following line at the top of index.js +// 在 index.js 顶部添加以下代码 exports.id = 'index'; ``` -When we now execute `index.js`, we’ll see these attributes as managed on each file’s `module` object: +然后运行 `index.js`,我们将看到: ``` ~/learn-node $ node index.js @@ -319,15 +303,15 @@ In util Module { ... } ``` -I’ve removed some attributes in the above output to keep it brief, but note how the `exports` object now has the attributes we defined in each module. You can put as many attributes as you want on that exports object, and you can actually change the whole object to be something else. For example, to change the exports object to be a function instead of an object, we do the following: +为了保持示例简短,我删除了以上输出中的一些属性,但请注意:`exports` 对象现在拥有我们在各模块中定义的属性。你可以向 exports 对象添加任意多的属性,也可以直接将整个对象改为其它对象。例如,我们可以通过以下方式将 exports 对象更改为一个函数: ``` -// Add the following line in index.js before the console.log +// 将以下代码添加在 index.js 中的 console.log 语句前 module.exports = function() {}; ``` -When you run `index.js` now, you’ll see how the `exports` object is a function: +再次运行 `index.js`,你将看到 `exports` 对象是一个函数: ``` ~/learn-node $ node index.js @@ -338,9 +322,9 @@ In index Module { ... } ``` -Note how we did not do `exports = function() {}` to make the `exports` object into a function. We can’t actually do that because the `exports` variable inside each module is just a reference to `module.exports` which manages the exported properties. When we reassign the `exports` variable, that reference is lost and we would be introducing a new variable instead of changing the `module.exports` object. +>注意:我们并没有使用 `exports = function() {}` 来将 `exports` 对象更改为函数。实际上,由于各模块中的 `exports` 变量仅仅是对管理输出属性的 `module.exports` 的引用,当我们对 `exports` 变量重新赋值时,引用就会丢失,此时我们只是引入了一个新的变量,而没有对 `module.exports` 做修改。 -The `module.exports` object in every module is what the `require` function returns when we require that module. For example, change the `require('./lib/util')` line in `index.js` into: +各模块中的 `module.exports` 对象就是我们在引入该模块时 `require` 函数的返回值。例如,我们将 `index.js` 中的 `require('./lib/util')` 改为: ``` const UTIL = require('./lib/util'); @@ -348,24 +332,24 @@ const UTIL = require('./lib/util'); console.log('UTIL:', UTIL); ``` -The above will capture the properties exported in `lib/util` into the `UTIL` constant. When we run `index.js` now, the very last line will output: +以上代码会将 `lib/util` 输出的属性赋值给 `UTIL` 常量。我们现在运行 `index.js`,最后一行将输出以下结果: ``` UTIL: { id: 'lib/util' } ``` -Let’s also talk about the `loaded` attribute on every module. So far, every time we printed a module object, we saw a `loaded` attribute on that object with a value of `false`. +我们再来谈谈各模块中的 `loaded` 属性。到目前为止我们打印的所有 module 对象中都有一个值为 `false` 的 `loaded` 属性。 -The `module` module uses the `loaded` attribute to track which modules have been loaded (true value) and which modules are still being loaded (false value). We can, for example, see the `index.js` module fully loaded if we print its `module` object on the next cycle of the event loop using a `setImmediate` call: +`module` 模块使用 `loaded` 属性对模块的加载状态进行跟踪,判断哪些模块已经加载完成(值为 true)以及哪些模块仍在加载(值为 false)。例如,要判断 `index.js` 模块是否已完全加载,我们可以在下一个事件循环中使用一个 `setImmediate` 回调打印出他的 `module` 对象。 ``` -// In index.js +// index.js 中 setImmediate(() => { console.log('The index.js module object is now loaded!', module) }); ``` -The output of that would be: +以上输出将得到: ``` The index.js module object is now loaded! Module { @@ -390,23 +374,26 @@ The index.js module object is now loaded! Module { '/node_modules' ] } ``` -Note how in this delayed `console.log` output both `lib/util.js` and `index.js` are fully loaded. +>注意:这个延迟的 `console.log` 的输出显示了 `lib/util.js` 和 `index.js` 都已完全加载。 The `exports` object becomes complete when Node finishes loading the module (and labels it so). The whole process of requiring/loading a module is *synchronous.* That’s why we were able to see the modules fully loaded after one cycle of the event loop. +`exports` 对象在 Node 完成引入/加载所有模块并标记时(becomes complete……构建完成??)。引入一个模块的整个过程是 **同步的**,因此我们才能在一个事件循环结束后看见模块被完全加载。 -This also means that we cannot change the `exports` object asynchronously. We can’t, for example, do the following in any module: +这也意味着我们无法异步地更改 `exports` 对象。例如,我们在任何模块中都无法执行以下操作: ``` fs.readFile('/etc/passwd', (err, data) => { if (err) throw err; - exports.data = data; // Will not work. + exports.data = data; // 无效 }); ``` #### Circular module dependency #### +#### 循环模块依赖 #### Let’s now try to answer the important question about circular dependency in Node: What happens when module 1 requires module 2, and module 2 requires module 1? +我们现在来回答关于 Node 中循环依赖的重要问题:当模块1 To find out, let’s create the following two files under `lib/`, `module1.js` and `module.js` and have them require each other: From 25b93838b268f4ba80d9fd573ff5a3ab465baf4c Mon Sep 17 00:00:00 2001 From: zhouzihanntu Date: Thu, 30 Mar 2017 22:13:50 +0800 Subject: [PATCH 007/638] update file --- ...-in-node-js-everything-you-need-to-know.md | 57 ++++++++++--------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/TODO/requiring-modules-in-node-js-everything-you-need-to-know.md b/TODO/requiring-modules-in-node-js-everything-you-need-to-know.md index 0f35c99a5d3..4074986cbae 100644 --- a/TODO/requiring-modules-in-node-js-everything-you-need-to-know.md +++ b/TODO/requiring-modules-in-node-js-everything-you-need-to-know.md @@ -266,7 +266,6 @@ In util Module { 重点来了,如果我们在 `lib/util` 模块中引入 `index` 主模块会发生什么?这就是 Node 中所支持的循环模块依赖关系。 -To understand it better, let’s first understand a few other concepts on the module object. 为了更好理解循环模块依赖,我们先来了解一些关于 module 对象的概念。 #### exports、module.exports 和模块异步加载 #### @@ -389,13 +388,11 @@ fs.readFile('/etc/passwd', (err, data) => { }); ``` -#### Circular module dependency #### #### 循环模块依赖 #### -Let’s now try to answer the important question about circular dependency in Node: What happens when module 1 requires module 2, and module 2 requires module 1? -我们现在来回答关于 Node 中循环依赖的重要问题:当模块1 +我们现在来回答关于 Node 中循环依赖的重要问题:当我们在模块1中引用模块2,在模块2中引用模块1时会发生什么? -To find out, let’s create the following two files under `lib/`, `module1.js` and `module.js` and have them require each other: +为了找到答案,我们在 `lib/` 下创建 `module1.js` 和 `module.js` 两个文件并让它们互相引用: ``` // lib/module1.js @@ -413,24 +410,25 @@ const Module1 = require('./module1'); console.log('Module1 is partially loaded here', Module1); ``` -When we run `module1.js` we see the following: +执行 `module1.js` 后,我们将看到: ``` ~/learn-node $ node lib/module1.js Module1 is partially loaded here { a: 1 } ``` -We required `module2` before `module1` was fully loaded, and since `module2` required `module1` while it wasn’t fully loaded, what we get from the `exports` object at that point are all the properties exported prior to the circular dependency. Only the `a` property was reported because both `b` and `c` were exported after `module2` required and printed `module1`. +我们在 `module1` 加载完成前引用了 `module2`,而此时 `module1` 尚未加载完,我们从当前的 `exports` 对象中得到的是在循环依赖之前导出的所有属性。这里只列出的只有属性 `a`,因为属性 `b` 和 `c` 都是在 `module2` 引入并打印了 `module1` 后导出的。 -Node keeps this really simple. During the loading of a module, it builds the `exports` object. You can require the module before it’s done loading and you’ll just get a partial exports object with whatever was defined so far. +Node 使这个过程变得非常简单。它在模块加载时构建 `exports` 对象。你可以在该模块完成加载前引用它,而你将得到此时已定义的部分导出对象。 #### JSON and C/C++ addons #### +#### 使用 JSON 和 C/C++ 插件 #### -We can natively require JSON files and C++ addon files with the require function. You don’t even need to specify a file extension to do so. +我们可以使用自带的 require 函数引用 JSON 文件和 C++ 插件。你甚至不需要为此去指定一个文件扩展。 -If a file extension was not specified, the first thing Node will try to resolve is a `.js` file. If it can’t find a `.js` file, it will try a `.json` file and it will parse the `.json` file if found as a JSON text file. After that, it will try to find a binary `.node` file. However, to remove ambiguity, you should probably specify a file extension when requiring anything other than `.js` files. +如果一个文件扩展未被声明,Node 会在第一时间解析 `.js` 文件。如果没有找到 `.js` 文件,它将继续寻找 `.json` 文件并在找到一个 JSON 文本文件后将其解析为 `.json` 文件。随后,Node 将会查找二进制的 `.node` 文件。但是为了避免歧义,你最好在引用除 `.js` 文件以外的文件类型时声明文件扩展。 -Requiring JSON files is useful if, for example, everything you need to manage in that file is some static configuration values, or some values that you periodically read from an external source. For example, if we had the following `config.json` file: +如果你需要在文件中放置的内容都是一些静态的配置信息,或定期从外部来源读取的一些值,那么使用 JSON 文件非常有用。例如,我们有以下 `config.json` 文件: ``` { @@ -439,28 +437,29 @@ Requiring JSON files is useful if, for example, everything you need to manage in } ``` -We can require it directly like this: +我们可以这样直接引用它: ``` const { host, port } = require('./config'); -``` - -console.log(`Server will run at [http://${host}:${port}`](http://$%7Bhost%7D:$%7Bport%7D`)); +console.log(`Server will run at [http://${host}:${port}](http://$%7Bhost%7D:$%7Bport%7D`)); -Running the above code will have this output: +``` +执行以上代码将输出以下结果: +``` Server will run at [http://localhost:8080](http://localhost:8080) +``` -If Node can’t find a `.js` or a `.json` file, it will look for a `.node` file and it would interpret the file as a compiled addon module. +如果 Node 找不到 `.js` 或 `.json` 文件,它将查找一个 `.node` 文件并将其解释为一个编译后的插件模块。 -The Node documentation site has a [sample addon file](https://nodejs.org/api/addons.html#addons_hello_world) which is written in C++. It’s a simple module that exposes a `hello()` function and the hello function outputs “world.” +Node 文档站点有一个用 C++ 编写的[插件示例](https://nodejs.org/api/addons.html#addons_hello_world),该示例模块提供了一个输出 “world” 的 `hello()` 函数。 -You can use the `node-gyp` package to compile and build the `.cc` file into a `.addon` file. You just need to configure a [binding.gyp](https://nodejs.org/api/addons.html#addons_building) file to tell `node-gyp` what to do. +你可以使用 `node-gyp` 插件将 `.cc` 文件编译成 `.addon` 文件。只需要配置一个 [binding.gyp](https://nodejs.org/api/addons.html#addons_building) 文件来告诉 `node-gyp` 要做什么。 -Once you have the `addon.node` file (or whatever name you specify in `binding.gyp`) then you can natively require it just like any other module: +有了 `addon.node` 文件(你可以在 `binding.gyp` 中声明任意文件名),你就可以像引用其他模块一样引用它了。 ``` const addon = require('./addon'); @@ -468,29 +467,31 @@ const addon = require('./addon'); console.log(addon.hello()); ``` -We can actually see the support of the three extensions by looking at `require.extensions`. +我们可以通过 `require.extensions` 查看 Node 对这三类扩展的支持。 -Looking at the functions for each extension, you can clearly see what Node will do with each. It uses `module._compile` for `.js` files, `JSON.parse` for `.json` files, and `process.dlopen` for `.node` files. +你可以从各个扩展对应的函数中清楚了解 Node 对它们分别所做的操作:对 `.js` 文件使用 `module._compile`,对 `.json` 文件使用 `JSON.parse`,对 `.node` 文件使用 `process.dlopen`。 #### All code you write in Node will be wrapped in functions #### +#### 你在 Node 中写的所有代码都将被包装成函数 #### -Node’s wrapping of modules is often misunderstood. To understand it, let me remind you about the `exports`/`module.exports` relation. +Node 的模块打包经常被误解。要了解它的原理,请回忆一下 `exports` 与 `module.exports` 的关系。 -We can use the `exports` object to export properties, but we cannot replace the `exports` object directly because it’s just a reference to `module.exports` +我们可以使用 `exports` 对象导出属性,但是由于 `exports` 对象仅仅是对 `module.exports` 的一个引用,我们无法直接对其执行替换操作。 ``` -exports.id = 42; // This is ok. +exports.id = 42; // 有效 -exports = { id: 42 }; // This will not work. +exports = { id: 42 }; // 无效 -module.exports = { id: 42 }; // This is ok. +module.exports = { id: 42 }; // 有效 ``` -How exactly does this `exports` object, which appears to be global for every module, get defined as a reference on the `module` object? +这个 `exports` 对象看起来对所有模块都是全局的,它是如何被定义成 `module` 对象的引用的呢? Let me ask one more question before explaining Node’s wrapping process. +在解释 Node 的打包进程前,再 In a browser, when we declare a variable in a script like this: From 656d9cbcd89d61778c2c2675c4e9b9156c5c3347 Mon Sep 17 00:00:00 2001 From: tanglie Date: Sat, 1 Apr 2017 18:05:07 +0800 Subject: [PATCH 008/638] Update Dependency-Injection-with-Dagger-2.md --- TODO/Dependency-Injection-with-Dagger-2.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/TODO/Dependency-Injection-with-Dagger-2.md b/TODO/Dependency-Injection-with-Dagger-2.md index 199a78c5438..f33534c9171 100644 --- a/TODO/Dependency-Injection-with-Dagger-2.md +++ b/TODO/Dependency-Injection-with-Dagger-2.md @@ -86,7 +86,7 @@ Retrofit retrofit = new Retrofit.Builder() 你需要通过创建 Dagger 2 **模块**定义哪些对象应该作为依赖链的一部分。例如,假设我们想要创建一个 `Retrofit` 单例,使它绑定到应用生命周期,对所有的 Activity 和 Fragment 都可用,我们首先需要使 Dagger 意识到他可以提供 `Retrofit` 的实例。 -Because we wish to setup caching, we need an Application context. Our first Dagger module, `AppModule.java`, will be used to provide this reference. We will define a method annotated with `@Provides` that denotes to Dagger that this method is the constructor for the `Application` return type: +因为需要设置缓存,我们需要一个 Application context。我们的第一个 Dagger 模块,`AppModule.java`,被用于提供这个依赖。我们将定义一个 `@Provides` 注解,标注带有 `Application` 的构造方法: ```java @Module @@ -106,9 +106,9 @@ public class AppModule { } ``` -We create a class called `NetModule.java` and annotate it with `@Module` to signal to Dagger to search within the available methods for possible instance providers. +我们创建了一个名为 `NetModule.java` 的类,并用 `@Module` 来通知 Dagger,在这里查找提供实例的方法。 -The methods that will actually expose available return types should also be annotated with `@Provides` decorator. The `Singleton` annotation also signals to the Dagger compiler that the instance should be created only once in the application. In the following example, we are specifying `SharedPreferences`, `Gson`, `Cache`, `OkHttpClient`, and `Retrofit` as the return types that can be used as part of the dependency list. +返回实例的方法也应当用 `@Provides` 标注。`Singleton` 标注通知 Dagger 编译器,实例在应用中只应被创建一次。在下面的例子中,我们把 `SharedPreferences`, `Gson`, `Cache`, `OkHttpClient`, 和 `Retrofit` 设置为在依赖列表中可用的类型。 ```java @Module @@ -166,7 +166,7 @@ public class NetModule { } ``` -Note that the method names (i.e. `provideGson()`, `provideRetrofit()`, etc) do not matter and can be named anything. The return type annotated with a `@Provides` decorator is used to associate this instantiation with any other modules of the same type. The `@Singleton` annotation is used to declare to Dagger to be only initialized only once during the entire lifecycle of the application. +注意,方法名称(比如 `provideGson()`, `provideRetrofit()` 等)是没关系的,可以任意设置。 用 `@Provides` decorator is used to associate this instantiation with any other modules of the same type. The `@Singleton` annotation is used to declare to Dagger to be only initialized only once during the entire lifecycle of the application.   A `Retrofit` instance depends both on a `Gson` and `OkHttpClient` instance, so we can define another method within the same class that takes these two types. The `@Provides` annotation and these two parameters in the method will cause Dagger to recognize that there is a dependency on `Gson` and `OkHttpClient` to build a `Retrofit` instance. From 1d7b8ca3579b0e402f04c76d8340454fb04027bc Mon Sep 17 00:00:00 2001 From: xiaoyusilen Date: Sat, 1 Apr 2017 23:49:40 +0800 Subject: [PATCH 009/638] First translation --- TODO/go-function-calls-redux.md | 51 +++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 9 deletions(-) diff --git a/TODO/go-function-calls-redux.md b/TODO/go-function-calls-redux.md index 2e8b6efa515..5e57a42f892 100644 --- a/TODO/go-function-calls-redux.md +++ b/TODO/go-function-calls-redux.md @@ -4,11 +4,17 @@ > * 译者:[xiaoyusilen](http://xiaoyu.world) > * 校对者: -# Go 函数调用 Redux # +# Go Function Calls Redux Go 方法调用 Redux # + +Some time ago in a [previous post](https://syslog.ravelin.com/anatomy-of-a-function-call-in-go-f6fc81b80ecc#.gpqsgzmjc) I promised to take a further look at how function calls and call stacks in Go work. I’ve think found a neat way to make good on that promise, so here goes. 前段时间在一篇[文章](https://syslog.ravelin.com/anatomy-of-a-function-call-in-go-f6fc81b80ecc#.gpqsgzmjc)中我答应写一篇进一步分析 Go 中如何进行函数调用和调用堆栈在 Go 中如何工作的文章。现在我找到了一种简洁的方式来向大家展示上述内容,所以有了现在这篇文章。 -什么是调用堆栈?它是一个用于保存局部变量和调用参数的内存区域,并且跟踪每个函数应该返回到哪里去。每个 goroutine 都有它自己的堆栈。你甚至可以说每个 goroutine 就是它自己的堆栈。 +So what is a call stack? Well, it’s an area of memory used to hold local variables and call parameters, and to track where functions should return to. Each goroutine has it’s own stack. You could almost say a goroutine is its stack. + +什么是调用堆栈?它是一个用于保存局部变量和调用参数的内存区域,并且跟踪每个方法应该返回到哪里去。每个 goroutine 都有它自己的堆栈。你甚至可以说每个 goroutine 就是它自己的堆栈。 + +Here’s the code I’m going to use to show the stack in action. It’s just a sequence of simple function calls. main() calls [f1(0xdeadbeef)](https://en.wikipedia.org/wiki/Hexspeak) , which then calls `f2(0xabad1dea)`, which calls `f3(0xbaddcafe)`. `f3()` then adds one to it’s parameter, and stores it in a local variable called `local`. It then takes the address of `local` and prints out memory starting at that address. Because `local` is on the stack, this prints the stack. 下面是我用于演示堆栈的代码。就是一系列简单的函数调用,main() 函数调用 [f1(0xdeadbeef)](https://en.wikipedia.org/wiki/Hexspeak),然后调用 `f2(0xabad1dea)`,再调用 `f3(0xbaddcafe)`。然后 f3() 将其中一个作为它的参数,并且将它存储在名为 `local` 的本地变量中。然后获取 `local` 的内存地址并且从那里开始输出。因为 `loacl` 在栈内,所以输出的就是栈。 @@ -59,8 +65,12 @@ func showFunc(at uintptr) { } ``` +Here’s the output of the program. It is a dump of memory starting at the address of `local`, shown as a list of 8-byte integers in hex. The address of each integer is on the left, and the int at the address is on the right. + 下面是上述代码的输出结果。它是从 `local` 的地址开始的内存转储,是以十六进制形式展示的8字节列表。左边是每个整数的存储地址,右边是地址内存储的整数。 +We know `local` should equal 0xBADDCAFE + 1, or 0xBADDCAFF, and this is indeed what we see at the start of the dump. + 我们知道 `local` 应该等于 0xBADDCAFE + 1,或者 0xBADDCAFF,这确实是我们转储开始时看到的。 ``` @@ -94,11 +104,16 @@ C42003FFC0: C4200001A0 102752A is runtime.main /usr/local/Cellar/go/1.8/libexec/src/runtime/proc.go 194 ``` +- The next number is 0xC42003FF48, which is the address of the 5th line of the dump. - 下一个数字是 0xC42003FF48,它是转储的第五行的地址。 +- After that we have 0x1088BEB. It turns out this is an address of executable code, and if we feed it into `runtime.FuncForPC` we see it is the address of line 19 of main.go, which is the last line of `f2()`. This is the address we return to when `f3()` returns. - 然后我们可以得到 0x1088BEB。事实上这是一个可执行代码的地址,如果我们将它作为 `runtime.FuncForPC` 的参数,我们知道它是 main.go 的第19行代码的地址,也是 f2() 的最后一行代码。这是 f3() 返回时我们得到的地址。 +- Next we have 0xBADDCAFE, the parameter to our call to `f3()` - 接下来我们得到 0xBADDCAFE,这是我们调用 `f3()` 时的参数。 -如果继续我们将看到类似上面的输出结果。下面我已经标记了内存转储,显示堆栈指针如何跟踪转储,参数和返回地址在哪里。 +If we carry on we continue to see this pattern. Below I’ve marked up the memory dump showing how the stack pointers track back through the dump and where the function parameters and return addresses sit. + +如果继续我们将继续看到这种模式。下面我已经标记了内存转储,显示堆栈指针如何跟踪转储,参数和返回地址在哪里。 ```go C42003FF28: BADDCAFF Local variable in f3() @@ -115,14 +130,22 @@ C42003FFC0: C4200001A0 C42003FF80: 102752A return to runtime.main() ``` -通过这些我们可以看出: +From this we can see many things. +通过这些我们可以明白很多。 + +- First, the stack starts at a high address, and the stack address reduces as function calls are made. - 首先,堆栈从高地址开始,堆栈地址随着函数调用变小。 -- 当进行函数调用时,调用者将参数放入栈内,然后返回地址(调用函数中的下一条指令的地址),接着指向堆栈中较高的指针。 +- When a function call is made, the caller pushes the parameters onto the stack, then the return address (the address of the next instruction in the calling function), then a pointer to a higher point in the stack. +- 当进行函数调用时,调用者将参数放入栈内,然后返回地址(调用函数中的下一条指令的地址),然后指向堆栈中较高的指针。 +- This pointer is used to find the previous function call on the stack when unwinding the stack when the call returns. - 当调用返回时,这个指针用于在堆栈中查找先前调用的函数。 +- Local variables are stored after the stack pointer. - 局部变量存储在堆栈指针之后。 -我们可以使用相同的技巧来分析一些稍微复杂的函数调用。这次,我添加了更多的参数,`f2()` 函数也返回了更多的值。 +We can use the same technique to look at some slightly more complicated function calls. I’ve added more parameters, and some return values to `f2()` in this version. + +我们可以使用相同的技巧来分析一些稍微复杂的函数调用。这次,我添加了更多的参数,`f2()` 方法也返回了更多的值。 ```go package main @@ -153,6 +176,8 @@ func f3(val int) { } ``` +This time I’ve jumped straight to the marked-up output. + 这次我们直接看被我标记好的输出结果。 ```go @@ -173,11 +198,19 @@ func f3(val int) { C42003FF80: 102752A return to runtime.main() ``` -从结果中我们可以看出: +From this we can see that -- 调用函数在函数参数之前为被调用函数的返回值提供空间。(注意这些值是没有初始化的,因为这个函数还没有返回!) +从结果中我们可以看出 + +- the calling function makes space for the return values of the called function before the function parameters. (Note the values are uninitialised because the function hasn’t returned yet!) +- 调用函数在函数参数之前为被调用函数的返回值提供空间。(注意这些值是没有初始化的,因为这个方法还没有返回!) +- parameters are pushed onto the stack in reverse order. - 参数在栈内的顺序与入栈顺序相反。 -希望我都讲清楚了。既然你已经看到这儿了,如果喜欢我的这篇文章或者可以从中学到一点什么的话,那么请给我点个赞。不然我就没办法获得积分。 +Hopefully all that made sense! If you got this far and enjoyed it or learned something, please hit that heart-button. I can’t earn internet points without you. + +希望上面所写的这些没什么问题!既然你已经看到这儿了,如果喜欢我的这篇文章或者可以从中学到一点什么的话,那么请给我点个赞。不然我就没办法获得积分。 + +*By day, Phil fights crime at* [*ravelin.com*](https://ravelin.com) *. You can join him:* [*https://angel.co/ravelin/jobs* **Phil 白天在 [ravelin.com](https://ravelin.com) 的工作主要是防止网上欺诈,你可以加入他 https://angel.co/ravelin/jobs。** \ No newline at end of file From a5d5bae6fc2fceca0c14d8acce080944cf044862 Mon Sep 17 00:00:00 2001 From: xiaoyusilen Date: Sat, 1 Apr 2017 23:57:27 +0800 Subject: [PATCH 010/638] Modify details --- TODO/anatomy-of-a-function-call-in-go.md | 108 +++++++++++------------ TODO/go-function-calls-redux.md | 51 ++--------- 2 files changed, 63 insertions(+), 96 deletions(-) diff --git a/TODO/anatomy-of-a-function-call-in-go.md b/TODO/anatomy-of-a-function-call-in-go.md index e03a90ccad1..b4baa299cb1 100644 --- a/TODO/anatomy-of-a-function-call-in-go.md +++ b/TODO/anatomy-of-a-function-call-in-go.md @@ -1,18 +1,18 @@ > * 原文地址:[Anatomy of a function call in Go](https://syslog.ravelin.com/anatomy-of-a-function-call-in-go-f6fc81b80ecc#.povigaliw) > * 原文作者:[Phil Pearl](https://syslog.ravelin.com/@philpearl?source=post_header_lockup) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 译者: +> * 译者:[xiaoyusilen](http://xiaoyu.world) > * 校对者: -# Anatomy of a function call in Go # +# 解析 Go 中的函数调用 # -Let’s take a look at a couple of simple Go functions and see if we can see how function calls work. We’ll do this by looking at the assembly language the Go compiler generates for the functions. This might be a little ambitious for a small blog post, but don’t worry, assembly language is simple. Even a CPU can understand it. +让我们来看一些简单的 Go 的函数,然后看看我们能否明白函数调用是怎么回事。我们将通过分析 Go 编译器根据函数生成的汇编语言来完成这件事。对于一个小小的博客来说这可能有一点野心,但是别担心,汇编语言很简单。哪怕是 CPU 都能读懂。 -By Rob Baines [https://github.com/telecoda/inktober-2016](https://github.com/telecoda/inktober-2016) +图片来自 Rob Baines [https://github.com/telecoda/inktober-2016](https://github.com/telecoda/inktober-2016) -Here’s our first function. Yep, we’re just adding two numbers. +这是我们的第一个函数。对,我们只是让两个数相加。 ``` func add(a, b int) int { @@ -20,9 +20,9 @@ func add(a, b int) int { } ``` -We’ll build it with optimisations disabled to make the assembly easier to follow. We do this with `go build -gcflags ‘-N -l’`. We can then dump out the assembly for our function with `go tool objdump -s main.add func` (func is the name of our package and the executable we just built with go build). +我们编译的时候需要关闭优化这样方便跟踪每个部分。我们用 `go build -gcflags 'N -l'` 这个命令来完成上述操作。然后我们可以用 `go tool objdump -s main.add func` 输出我们函数的具体细节(这里的 func 是我们的包名,也就是我们刚刚用 go build 编译出的可执行文件)。 -If you’ve never looked at assembly language before, then, well, congratulations, this is a new thing for you today. I’ve done this on a Mac, so the assembly is Intel 64-bit. +如果你以前从来没有看过汇编语言,那么恭喜啦,你今天看到的内容对你来说是全新的。我将在 Mac 上完成这些事情,所以配置是 Intel 64位。 ``` main.go:20 0x22c0 48c744241800000000 MOVQ $0x0, 0x18(SP) @@ -33,43 +33,43 @@ If you’ve never looked at assembly language before, then, well, congratulation main.go:21 0x22db c3 RET ``` -What are we looking at here? Each line breaks down into 4 sections as follows. +我们看到了什么?每一行如下所示被分为了4部分: -- The source file name and line number (main.go:15). The source code at this line is translated to the instructions marked with that line number. One line of Go is likely translated to multiple lines of assembly. -- the offset into the object file (e.g. 0x22C0). -- The machine code (e.g. 48c744241800000000). This is the actual binary machine code the CPU executes. We’re not going to look at this! Almost nobody ever does. -- The assembly language representation of the machine code. This is the bit we can hope to understand. +- 源文件的名称和行号(main.go:15)。这行的源代码会被转换为标有代码行号的说明。Go 的一行可能被转换成多行程序集。 +- 对象的偏移量(例如 0x22C0)。 +- 机器码(例如 48c744241800000000)。这是 CPU 实际执行的二进制机器码。我们不需要看这个,几乎没有人看这玩意。 +- 机器码的汇编表示形式,这也是我们想要理解的部分。 -Let’s focus in on that last section, the assembly code. +让我们将注意力集中在最后一部分,汇编语言。 -- MOVQ, ADDQ and RET are instructions. They tell the CPU what operation to perform. They are followed by parameters telling the CPU what to perform the operation on. -- SP, AX and CX are CPU registers. These are places where the CPU can store working values. There are several other registers the CPU can use. -- SP is a special purpose register and is used to store the current stack pointer. The stack is an area of memory where the local variables, function parameters and function calls are recorded. There’s one stack per goroutine. As one function calls another, then another, each gets its own area on the stack. Areas are created during the function call by reducing the value of SP by the size of the area needed. -- 0x8(SP) refers to the memory location that is 8 bytes past the memory location that SP points to. +- MOVQ,ADDQ 和 RET 是说明。它们告诉 CPU 需要执行的操作。后面的参数告诉 CPU 对什么执行该操作。 +- SP,AX 和 CX 是 CPU 寄存器。寄存器是 CPU 用于存储值的地方,CPU 有多个寄存器可以使用。 +- SP 是一个专用寄存器,用于存储当前堆栈指针。堆栈是记录局部变量,参数和函数调用的寄存器。每个 goroutine 都有一个堆栈。当一个函数调用另一个函数,然后另一个函数再调用其他函数,每个函数在堆栈上获得自己的存储区域。在函数调用期间创建存储区域,将 SP 的大小中减去所需的存储大小。 +- 0x8(SP)是指超过 SP 指向的内存位置的 8 个字节的内存位置。 -So, our ingredients are memory locations, CPU registers, instructions to move values between memory and registers, and operations on registers. And that’s pretty much all a CPU does. +因此,我们的工作的内容包含存储器位置,CPU 寄存器,用于在存储器和寄存器之间移动值的指令以及寄存器上的操作。 这几乎就是一个 CPU 所完成的事情了。 -Now lets look at the assembly in detail, starting with the first instruction. Remember we have two parameters `a` & `b` which we need to load from memory somewhere, add together, then return to the caller somehow. +现在让我们从第一条指令开始看每一条内容。别忘了我们需要从内存中加载两个参数 `a` 和 `b`,把它们相加,然后返回至调用函数。 -1. `MOVQ $0x0, 0x18(SP)` puts 0 in the memory location SP+0x18. This is a bit mysterious. -2. `MOVQ 0x8(SP), AX` puts the contents of memory location SP+0x8 in CPU register AX. Perhaps this is loading one of our parameters from memory? -3. `MOVQ 0x10(SP), CX` puts the contents of memory location SP+0x10 in CPU register CX. This could be our other parameter. -4. `ADDQ CX, AX` adds CX to AX, leaving the result in AX. Well, that’s adding the two parameters surely. -5. `MOVQ AX, 0x18(SP)` stores the contents of register AX at the memory location SP+0x18. And that’s saving the result of the addition. -6. `RET` returns to the calling function. +1. `MOVQ $0x0, 0x18(SP)` 将 0 置于存储单元 SP+0x18 中。 这句看起来有点神秘。 +2. `MOVQ 0x8(SP), AX` 将存储单元 SP+0x8 中的内容放到 CPU 寄存器 AX 中。也许这就是从内存中加载的我们的参数之一? +3. `MOVQ 0x10(SP), CX` 将存储单元 SP+0x10 的内容置于 CPU 寄存器 CX 中。 这可能就是我们所需的另一个参数。 +4. `ADDQ CX, AX` 将 CX 与 AX 相加,将结果存到 AX 中。好,现在已经把两个参数相加了。 +5. `MOVQ AX, 0x18(sp)` 将寄存器 AX 的内容存储在存储单元 SP+0x18 中。这就是在存储相加的结果。 +6. `RET` 将结果返回至调用函数。 -Remember our function has two parameters `a` & `b`, and it calculates `a+b` and returns the result. `MOVQ 0x8(SP), AX` is moving parameter `a` to AX. `a` is passed into the function on the stack at SP+0x8. `MOVQ 0x10(SP), CX` moves parameter `b` to CX. Parameter `b` is passed into the function on the stack at SP+0x10. `ADDQ CX, AX` adds `a` & `b`. `MOVQ AX, 0x18(SP)` stores the result at SP+0x18. The result is passed out of the function by placing it on the stack at SP+0x18. When the function returns the calling function can read the result off the stack. +记住我们的函数有两个参数 `a` 和 `b`,它计算了 `a+b` 并且返回了结果。`MOVQ 0x8(SP), AX` 将参数 `a` 移到 AX 中,在 SP+0x8 的堆栈中 `a` 将被传给函数。`MOVQ 0x10(SP), CX` 将参数 `b` 移到 CX 中,在 SP+0x10 的堆栈中 `b` 将被传给函数。`ADDQ CX, AX` 使 `a` 和 `b` 相加。`MOVQ AX, 0x18(SP)` 将结果存储到 SP+0x18 中。 现在相加的结果被存储在 SP+0x18 的堆栈中,当函数返回调用函数时,可以从栈中读取结果。 -[I’ve assumed `a` is the first parameter and `b` is the second. I’m not sure that’s right. We’d need to play around a bit to work that out, but this post is getting pretty long already] +我假设 `a` 是第一个参数,`b` 是第二个参数。我不确定是不是这样。我们需要花一点时间来完成这件事,但是这篇文章已经很长了。 -So what’s that mysterious first line doing? `MOVQ $0x0, 0x18(SP)` is storing 0 in location SP+0x18, which is where our result is finally stored. We can guess that this is because Go sets uninitialised values to zero, and we’ve turned off optimisations so the compiler still does this even if it’s unnecessary. +那么有点神秘的第一行代码究竟是做什么用的?`MOVQ $0X0, 0X18(SP)` 将 0 存储至 SP+0x18 中,而 SP+0x18 是我们存储相加结果的地方。我们可以猜测,这是因为 Go 把没有初始化的值设置为 0 ,我们已经关闭了优化,即使没有必要,编译器也会执行这个操作。 -So what have we learnt. +所以我们从中明白了什么: -- Well, it looks like parameters are stored on the stack, with the first at SP+0x8, and the others at following at higher-numbered addresses. -- And it looks like returned values are stored after the parameters, at yet-higher still addresses. +- 好,看起来参数都存在堆栈中,第一个参数存储在 SP+0x8 中,另一个在更高编号的地址中。 +- 并且看上去返回的结果存储在参数后边,一个更高编号的已知地址中。 -Let’s now look at another function. This one has a local variable, but we’ve still kept it simple. +现在让我们看另一个函数。这个函数有一个全局变量,不过我们依然会让它看起来很简单。 ``` func add3(a int) int { @@ -78,7 +78,7 @@ func add3(a int) int { } ``` -We use the same procedure to get an assembly listing. +我们用和刚才一样的过程来获取程序集列表。 ``` TEXT main.add3(SB) @@ -98,37 +98,37 @@ TEXT main.add3(SB) main.go:17 0x22b6 c3 RET ``` -Oh! That looks quite a bit more complicated. Let’s try to work it out. +喔!看起来有点复杂。让我们来试试。 -The first 4 instructions are listed against source code line 15. Here is that line: +前4条指令是根据源代码中的第15行列出的。这行代码是这样的: ``` func add3(a int) int { ``` -That line doesn’t seem to do much. So perhaps this is some kind of function preamble. Let’s break it down. +这一行代码似乎没有做什么。所以这可能是一种声明函数的方法。让我们分析一下。 -- `SUBQ $0x10, SP` subtracts 0x10=16 from SP. This gives us 16 bytes more space on the stack -- `MOVQ BP, 0x8(SP)` stores the value of the register BP at SP+8, then `LEAQ 0x8(SP), BP` loads the address SP+8 into BP. So we’ve made a space to store the old value of BP, then loaded BP with the address of that space. This helps establish the chain of stack areas (or *stack frames*). This is a bit mysterious, and I’m afraid we won’t solve this in this post. -- Finally in this section we have `MOVQ $0x0, 0x20(SP)` which, similar to the last function we considered, initialises the return value to 0. +- `SUBQ $0x10, SP` 从 SP 减去 0x10=16。这个操作为我们分类了 16 字节的堆栈空间。 +- `MOVQ BP, 0x8(SP)` 将寄存器 BP 中的值存储至 SP+8 中,然后 `LEAQ 0x8(SP), BP` 将地址 SP+8 中的内容加载到 BP 中。现在我们已经有空间可以存储 BP 中之前所存的内容,然后将 BP 中的内容存储至刚刚分配的存储空间中,这有助于建立堆栈区域链(或者堆栈框架)。这有点神秘,不过在这篇文章中我们恐怕不会解决这个问题。 +- 在这一部分的最后是 `MOVQ $ 0x0, 0x20 (SP)`,它和我们刚刚分析的最后一句类似,就是将返回值初始化为0。 -The next line of the assembly corresponds to `b := 3` in the source code. The instruction is `MOVQ $0x3, 0(SP)`, which puts the value 3 into memory at SP+0. This solves one mystery. When we subtracted 0x10=16 from SP we made room for 2 8-byte values: our local variable `b` stored at SP+0, and the old value of BP stored at SP+0x08. +下一行对应的是源码中的 `b := 3`,`MOVQ $03x, 0(SP)` 把 3 放到 SP+0 中。这解决了我们的一个疑惑。当我们从 SP 中减去 0x10 = 16 时,我们得到了可以存储两个 8 字节值的空间:我们的局部变量 `b` 存储在 SP+0 中,而 BP 之前的值存储在 SP+0x08 中。 -The next 6 lines of assembly correspond to `return a + b`. This needs to cover loading `a` & `b` from memory, adding them, and returning the result. Let’s look at each line in turn. +接下来的 6 行程序集对应于 `return a + b`。这需要从内存中加载 `a` 和 `b`,然后将它们相加,并且返回结果。让我们依次看看每一行。 -- `MOVQ 0x18(SP), AX` moves the function parameter `a` stored at SP+0x18 into register AX -- `ADDQ $0x3, AX` adds 3 to AX (for some reason it doesn’t use our local variable `b` at SP+0, even though optimisations are turned off) -- `MOVQ AX, 0x20(SP)` stores the result of `a+b` at SP+0x20, which is where our return value is stored. -- Next we have `MOVQ 0x8(SP), BP` and `ADDQ $0x10, SP`. These restore the old value of BP, then adds 0x10 to SP, setting it back to the value it was at the start of the function. -- Finally we have `RET`, which returns to the caller. +- `MOVQ 0x18(SP), AX` 将存储在 SP+0x18 的参数 `a` 移动到寄存器 AX 中 +- `ADDQ $0x3, AX` 将 3 加到 AX(由于某些原因,它不使用我们存储在 SP+0 的局部变量 `b`,尽管编译时优化被关闭了) +- `MOVQ AX, 0x20(SP)` 将 `a+b` 的结果存储到 SP+0x20 中,也就是我们返回结果所存的地方。 +- 接下来我们得到的是 `MOVQ 0x8(SP), BP` 以及 `ADDQ $0x10, SP`,这些将恢复BP的旧值,然后将 0x10 添加到 SP,将其设置为该函数开始时的值。 +- 最后我们得到了 `RET`,将要返回给调用函数的。 -So what have we learnt? +所以我们从中学到了什么呢? -- The calling function makes space on the stack for the returned values and function parameters. The space for returned values is higher up the stack than the parameters. -- If the called function has local variables it makes room for them by decreasing the value of the stack pointer SP. It also does something mysterious with the register BP. -- When the function returns any manipulations of SP & BP are reversed. +- 调用函数在堆栈中为返回值和参数分配空间。返回值的存储地址比参数的存储地址高。 +- 如果被调用函数有局部变量,则通过减少堆栈指针 SP 的值为它们分配空间。它也和寄存器 BP 做了一些神秘的事情。 +- 当函数返回任何 SP&BP 的操作都会相反。 -Let’s map out how the stack was used in the add3() function: +让我们看看堆栈在 add3() 方法中如何使用: ``` SP+0x20: the return value @@ -145,8 +145,8 @@ SP+0x08: the old value of BP SP+0x0: the local variable b ``` -If you look we didn’t see any mention of SP+0x10, so we don’t *know* what this is used for. But I can tell you that this is where the return address is stored. This is how the `RET` instruction knows where to return to. +如果你觉得我们对于 SP+0x10 没有任何说明,所以不知道这是干什么用的。我可以告诉你,这是存储返回地址的地方。这是为了让 `RET` 指令知道返回到哪里去。 -Well, that’s enough for one post. Hopefully if you didn’t know how this stuff worked you now feel you understand it a little more, and if you were intimidated by assembly it’s perhaps a little less opaque. If you’d like any more detail on this stuff please write a comment and I’ll consider it for a future post. +这篇文章已经足够了。 希望如果以前你不知道这些东西如何工作,但是现在你觉得你已经有了一些了解,或者如果你被汇编吓倒了,那么它可能不那么清晰。 如果你想了解有关汇编的更多信息,请在评论中告诉我,我会考虑在之后的文章中写出来。 -If you got this far and enjoyed it or learned something, please hit that heart-button so other people can find it. +既然你已经看到这儿了,如果喜欢我的这篇文章或者可以从中学到一点什么的话,那么请给我点个赞这样这篇文章就可以被更多人看到了。 \ No newline at end of file diff --git a/TODO/go-function-calls-redux.md b/TODO/go-function-calls-redux.md index 5e57a42f892..2e8b6efa515 100644 --- a/TODO/go-function-calls-redux.md +++ b/TODO/go-function-calls-redux.md @@ -4,17 +4,11 @@ > * 译者:[xiaoyusilen](http://xiaoyu.world) > * 校对者: -# Go Function Calls Redux Go 方法调用 Redux # - -Some time ago in a [previous post](https://syslog.ravelin.com/anatomy-of-a-function-call-in-go-f6fc81b80ecc#.gpqsgzmjc) I promised to take a further look at how function calls and call stacks in Go work. I’ve think found a neat way to make good on that promise, so here goes. +# Go 函数调用 Redux # 前段时间在一篇[文章](https://syslog.ravelin.com/anatomy-of-a-function-call-in-go-f6fc81b80ecc#.gpqsgzmjc)中我答应写一篇进一步分析 Go 中如何进行函数调用和调用堆栈在 Go 中如何工作的文章。现在我找到了一种简洁的方式来向大家展示上述内容,所以有了现在这篇文章。 -So what is a call stack? Well, it’s an area of memory used to hold local variables and call parameters, and to track where functions should return to. Each goroutine has it’s own stack. You could almost say a goroutine is its stack. - -什么是调用堆栈?它是一个用于保存局部变量和调用参数的内存区域,并且跟踪每个方法应该返回到哪里去。每个 goroutine 都有它自己的堆栈。你甚至可以说每个 goroutine 就是它自己的堆栈。 - -Here’s the code I’m going to use to show the stack in action. It’s just a sequence of simple function calls. main() calls [f1(0xdeadbeef)](https://en.wikipedia.org/wiki/Hexspeak) , which then calls `f2(0xabad1dea)`, which calls `f3(0xbaddcafe)`. `f3()` then adds one to it’s parameter, and stores it in a local variable called `local`. It then takes the address of `local` and prints out memory starting at that address. Because `local` is on the stack, this prints the stack. +什么是调用堆栈?它是一个用于保存局部变量和调用参数的内存区域,并且跟踪每个函数应该返回到哪里去。每个 goroutine 都有它自己的堆栈。你甚至可以说每个 goroutine 就是它自己的堆栈。 下面是我用于演示堆栈的代码。就是一系列简单的函数调用,main() 函数调用 [f1(0xdeadbeef)](https://en.wikipedia.org/wiki/Hexspeak),然后调用 `f2(0xabad1dea)`,再调用 `f3(0xbaddcafe)`。然后 f3() 将其中一个作为它的参数,并且将它存储在名为 `local` 的本地变量中。然后获取 `local` 的内存地址并且从那里开始输出。因为 `loacl` 在栈内,所以输出的就是栈。 @@ -65,12 +59,8 @@ func showFunc(at uintptr) { } ``` -Here’s the output of the program. It is a dump of memory starting at the address of `local`, shown as a list of 8-byte integers in hex. The address of each integer is on the left, and the int at the address is on the right. - 下面是上述代码的输出结果。它是从 `local` 的地址开始的内存转储,是以十六进制形式展示的8字节列表。左边是每个整数的存储地址,右边是地址内存储的整数。 -We know `local` should equal 0xBADDCAFE + 1, or 0xBADDCAFF, and this is indeed what we see at the start of the dump. - 我们知道 `local` 应该等于 0xBADDCAFE + 1,或者 0xBADDCAFF,这确实是我们转储开始时看到的。 ``` @@ -104,16 +94,11 @@ C42003FFC0: C4200001A0 102752A is runtime.main /usr/local/Cellar/go/1.8/libexec/src/runtime/proc.go 194 ``` -- The next number is 0xC42003FF48, which is the address of the 5th line of the dump. - 下一个数字是 0xC42003FF48,它是转储的第五行的地址。 -- After that we have 0x1088BEB. It turns out this is an address of executable code, and if we feed it into `runtime.FuncForPC` we see it is the address of line 19 of main.go, which is the last line of `f2()`. This is the address we return to when `f3()` returns. - 然后我们可以得到 0x1088BEB。事实上这是一个可执行代码的地址,如果我们将它作为 `runtime.FuncForPC` 的参数,我们知道它是 main.go 的第19行代码的地址,也是 f2() 的最后一行代码。这是 f3() 返回时我们得到的地址。 -- Next we have 0xBADDCAFE, the parameter to our call to `f3()` - 接下来我们得到 0xBADDCAFE,这是我们调用 `f3()` 时的参数。 -If we carry on we continue to see this pattern. Below I’ve marked up the memory dump showing how the stack pointers track back through the dump and where the function parameters and return addresses sit. - -如果继续我们将继续看到这种模式。下面我已经标记了内存转储,显示堆栈指针如何跟踪转储,参数和返回地址在哪里。 +如果继续我们将看到类似上面的输出结果。下面我已经标记了内存转储,显示堆栈指针如何跟踪转储,参数和返回地址在哪里。 ```go C42003FF28: BADDCAFF Local variable in f3() @@ -130,22 +115,14 @@ If we carry on we continue to see this pattern. Below I’ve marked up the memor C42003FF80: 102752A return to runtime.main() ``` -From this we can see many things. +通过这些我们可以看出: -通过这些我们可以明白很多。 - -- First, the stack starts at a high address, and the stack address reduces as function calls are made. - 首先,堆栈从高地址开始,堆栈地址随着函数调用变小。 -- When a function call is made, the caller pushes the parameters onto the stack, then the return address (the address of the next instruction in the calling function), then a pointer to a higher point in the stack. -- 当进行函数调用时,调用者将参数放入栈内,然后返回地址(调用函数中的下一条指令的地址),然后指向堆栈中较高的指针。 -- This pointer is used to find the previous function call on the stack when unwinding the stack when the call returns. +- 当进行函数调用时,调用者将参数放入栈内,然后返回地址(调用函数中的下一条指令的地址),接着指向堆栈中较高的指针。 - 当调用返回时,这个指针用于在堆栈中查找先前调用的函数。 -- Local variables are stored after the stack pointer. - 局部变量存储在堆栈指针之后。 -We can use the same technique to look at some slightly more complicated function calls. I’ve added more parameters, and some return values to `f2()` in this version. - -我们可以使用相同的技巧来分析一些稍微复杂的函数调用。这次,我添加了更多的参数,`f2()` 方法也返回了更多的值。 +我们可以使用相同的技巧来分析一些稍微复杂的函数调用。这次,我添加了更多的参数,`f2()` 函数也返回了更多的值。 ```go package main @@ -176,8 +153,6 @@ func f3(val int) { } ``` -This time I’ve jumped straight to the marked-up output. - 这次我们直接看被我标记好的输出结果。 ```go @@ -198,19 +173,11 @@ This time I’ve jumped straight to the marked-up output. C42003FF80: 102752A return to runtime.main() ``` -From this we can see that +从结果中我们可以看出: -从结果中我们可以看出 - -- the calling function makes space for the return values of the called function before the function parameters. (Note the values are uninitialised because the function hasn’t returned yet!) -- 调用函数在函数参数之前为被调用函数的返回值提供空间。(注意这些值是没有初始化的,因为这个方法还没有返回!) -- parameters are pushed onto the stack in reverse order. +- 调用函数在函数参数之前为被调用函数的返回值提供空间。(注意这些值是没有初始化的,因为这个函数还没有返回!) - 参数在栈内的顺序与入栈顺序相反。 -Hopefully all that made sense! If you got this far and enjoyed it or learned something, please hit that heart-button. I can’t earn internet points without you. - -希望上面所写的这些没什么问题!既然你已经看到这儿了,如果喜欢我的这篇文章或者可以从中学到一点什么的话,那么请给我点个赞。不然我就没办法获得积分。 - -*By day, Phil fights crime at* [*ravelin.com*](https://ravelin.com) *. You can join him:* [*https://angel.co/ravelin/jobs* +希望我都讲清楚了。既然你已经看到这儿了,如果喜欢我的这篇文章或者可以从中学到一点什么的话,那么请给我点个赞。不然我就没办法获得积分。 **Phil 白天在 [ravelin.com](https://ravelin.com) 的工作主要是防止网上欺诈,你可以加入他 https://angel.co/ravelin/jobs。** \ No newline at end of file From cb6799660a7a9feae5598380fd5e43009b9b8b96 Mon Sep 17 00:00:00 2001 From: xiaoyusilen Date: Sat, 1 Apr 2017 23:59:45 +0800 Subject: [PATCH 011/638] Modify details --- TODO/anatomy-of-a-function-call-in-go.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO/anatomy-of-a-function-call-in-go.md b/TODO/anatomy-of-a-function-call-in-go.md index b4baa299cb1..f6141663c05 100644 --- a/TODO/anatomy-of-a-function-call-in-go.md +++ b/TODO/anatomy-of-a-function-call-in-go.md @@ -145,7 +145,7 @@ SP+0x08: the old value of BP SP+0x0: the local variable b ``` -如果你觉得我们对于 SP+0x10 没有任何说明,所以不知道这是干什么用的。我可以告诉你,这是存储返回地址的地方。这是为了让 `RET` 指令知道返回到哪里去。 +如果你觉得文章中没有提到 SP+0x10,所以不知道这是干什么用的。我可以告诉你,这是存储返回地址的地方。这是为了让 `RET` 指令知道返回到哪里去。 这篇文章已经足够了。 希望如果以前你不知道这些东西如何工作,但是现在你觉得你已经有了一些了解,或者如果你被汇编吓倒了,那么它可能不那么清晰。 如果你想了解有关汇编的更多信息,请在评论中告诉我,我会考虑在之后的文章中写出来。 From 96b15b70ae9c88af6f594c11b3c80a2454821c49 Mon Sep 17 00:00:00 2001 From: zhuzi Date: Sun, 2 Apr 2017 02:02:36 +0800 Subject: [PATCH 012/638] translation first half --- TODO/beyond-browser-web-desktop-apps.md | 222 +++++++++++++++++------- 1 file changed, 161 insertions(+), 61 deletions(-) diff --git a/TODO/beyond-browser-web-desktop-apps.md b/TODO/beyond-browser-web-desktop-apps.md index 57d1a9da13e..a0dfb366c18 100644 --- a/TODO/beyond-browser-web-desktop-apps.md +++ b/TODO/beyond-browser-web-desktop-apps.md @@ -1,55 +1,83 @@ > * 原文地址:[Beyond The Browser: From Web Apps To Desktop Apps](https://www.smashingmagazine.com/2017/03/beyond-browser-web-desktop-apps/) > * 原文作者:[Adam Lynch](https://www.smashingmagazine.com/author/adamlynch/) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 译者: +> * 译者: [bambooom](https://github.com/bambooom) > * 校对者: -## Beyond The Browser: From Web Apps To Desktop Apps +> ## Beyond The Browser: From Web Apps To Desktop Apps -I started out as a web developer, and that’s now one part of what I do as a full-stack developer, but never had I imagined I’d create things for the desktop. I love the web. I love how altruistic our community is, how it embraces open-source, testing and pushing the envelope. I love discovering beautiful websites and powerful apps. When I was first tasked with creating a desktop app, I was apprehensive and intimidated. It seemed like it would be difficult, or at least… different. +## 超越浏览器:从 web 应用到桌面应用 -It’s not an attractive prospect, right? Would you have to learn a new language or three? Imagine an archaic, alien workflow, with ancient tooling, and none of those things you love about the web. How would your career be affected? +> I started out as a web developer, and that’s now one part of what I do as a full-stack developer, but never had I imagined I’d create things for the desktop. I love the web. I love how altruistic our community is, how it embraces open-source, testing and pushing the envelope. I love discovering beautiful websites and powerful apps. When I was first tasked with creating a desktop app, I was apprehensive and intimidated. It seemed like it would be difficult, or at least… different. -OK, take a breath. The reality is that, as a web developer, not only do you already possess all of the skills to make great modern desktop apps, but thanks to powerful new APIs at your disposal, the desktop is actually where your skills can be leveraged the most. +一开始我是个 web 开发者,现在我是个全栈开发者,但从未想过在桌面上有所作为。我喜欢 web,我热爱这个无私的社区,热爱它对于开源的友好,测试并推向更远的地方?我喜欢发现好看的网站和强大的应用。当我被指派做桌面应用的任务的时候,我f非常忧虑和害怕,因为那看起来很难,或者至少非常不同。 -In this article, we’ll look at the development of desktop applications using [NW.js](http://nwjs.io/) and [Electron](http://electron.atom.io/), the ups and downs of building one and living with one, using one code base for the desktop and the web, and more. +> It’s not an attractive prospect, right? Would you have to learn a new language or three? Imagine an archaic, alien workflow, with ancient tooling, and none of those things you love about the web. How would your career be affected? -#### Further Reading on SmashingMag: +这并不吸引人,对吧?你需要学一门新的语言,甚至三种?想象一下过时的工作流,古旧的工具,没有任何你喜欢的有关 web 的一切。你的职业会怎样被影响呢? -- [Pixel-Perfect Specifications Without The Headaches](https://www.smashingmagazine.com/2016/08/pixel-perfect-specifications-without-the-headaches/) -- [Building A First-Class App That Leverages Your Website](https://www.smashingmagazine.com/2016/02/building-first-class-app-leverages-website-case-study/) -- [Mobile Considerations in UX Design: “Web or Native?”](https://www.smashingmagazine.com/2012/06/mobile-considerations-in-user-experience-design-web-or-native/) -- [A Beginner’s Guide To Progressive Web Apps](https://www.smashingmagazine.com/2016/08/a-beginners-guide-to-progressive-web-apps/) +> OK, take a breath. The reality is that, as a web developer, not only do you already possess all of the skills to make great modern desktop apps, but thanks to powerful new APIs at your disposal, the desktop is actually where your skills can be leveraged the most. -### Why? +别慌,深呼吸,现实情况是,作为 web 开发者,你已经拥有一切开发现代桌面应用所需的一切技能,甚至感谢新的强大的 API,你可以在桌面应用中发挥你最大的潜能。 -First of all, why would anyone create a desktop app? Any existing web app (as opposed to a website, if you believe in the distinction) is probably suited to becoming a desktop app. You could build a desktop app around any web app that would benefit from integration in the user’s system; think native notifications, launching on startup, interacting with files, etc. Some users simply prefer having certain apps there permanently on their machine, accessible whether they have a connection or not. +> In this article, we’ll look at the development of desktop applications using [NW.js](http://nwjs.io/) and [Electron](http://electron.atom.io/), the ups and downs of building one and living with one, using one code base for the desktop and the web, and more. -Maybe you’ve an idea that would only work as a desktop app; some things simply aren’t possible with a web app (at least yet, but more about that in a little bit). You could create a self-contained utility app for internal company use, without requiring anyone to install anything other than your app (because Node.js in built-in). Maybe you’ve an idea for the Mac App Store. Maybe it would simply be a fun side project. +本文将会介绍使用 [NW.js](http://nwjs.io/) 和 [Electron](https://electron.atom.io/) 开发桌面应用,它们的优劣,使用同一套基准代码给桌面、web,甚至更多。 -It’s hard to sum up why you should consider creating a desktop app because there are so many kinds of apps you could create. It really depends on what you’d like to achieve, how advantageous you find the additional APIs, and how much offline usage would enhance the experience for your users. For my team, it was a no-brainer because we were building a [chat application](https://teamwork.com/chat). On the other hand, a connection-dependent desktop app that doesn’t really have any desktop integration should be a web app and a web app alone. It wouldn’t be fair to expect a user to download your app (which includes a browser of its own and Node.js) when they wouldn’t get any more value from it than from visiting a URL of yours in their favorite browser. +> ### Why? +### 为什么? -Instead of describing the desktop app you personally should build and why, I’m hoping to spark an idea or at least spark your interest in this article. Read on to see just how easy it is to create powerful desktop apps using web technology and what that can afford you over (or alongside of) creating a web app. +> First of all, why would anyone create a desktop app? Any existing web app (as opposed to a website, if you believe in the distinction) is probably suited to becoming a desktop app. You could build a desktop app around any web app that would benefit from integration in the user’s system; think native notifications, launching on startup, interacting with files, etc. Some users simply prefer having certain apps there permanently on their machine, accessible whether they have a connection or not. + +首先,为什么会有人开发桌面应用?任何现有的 web 应用(不同于网站,如果你认为它们是不同的)都可能适合变成一个桌面应用。你可以为任何可以受益于与用户系统整合的 web 应用制作桌面应用;例如本地通知,开机启动,与文件的交互等。有些用户单纯更喜欢在自己的电脑中永久拥有一些 app,无论是否联网都可以使用。 + +> Maybe you’ve an idea that would only work as a desktop app; some things simply aren’t possible with a web app (at least yet, but more about that in a little bit). You could create a self-contained utility app for internal company use, without requiring anyone to install anything other than your app (because Node.js in built-in). Maybe you’ve an idea for the Mac App Store. Maybe it would simply be a fun side project. + +也许你有个想法,但只能在桌面应用上有意义,有些事情只是在 web 应用中不可能(至少是这样,但也可能更多一点(译注:没太懂这一句))。你可能想要创建一个独立的多功能应用,只给公司内部使用,除了你的 app 不需要安装其他东西(因为内置 Node.js )。也许你有个有关 Mac 应用商店的想法,也许只是你的一个个人兴趣的小项目。 + +> It’s hard to sum up why you should consider creating a desktop app because there are so many kinds of apps you could create. It really depends on what you’d like to achieve, how advantageous you find the additional APIs, and how much offline usage would enhance the experience for your users. For my team, it was a no-brainer because we were building a [chat application](https://teamwork.com/chat). On the other hand, a connection-dependent desktop app that doesn’t really have any desktop integration should be a web app and a web app alone. It wouldn’t be fair to expect a user to download your app (which includes a browser of its own and Node.js) when they wouldn’t get any more value from it than from visiting a URL of yours in their favorite browser. + +很难概括你应该考虑开发桌面应用的原因,因为真的有很多很多东西你可以做。这非常取决于你想要达到什么,API 是否足够有利于开发,离线使用将多大程度上改进用户体验。在我的团队,这些都是毋庸置疑的,因为我们在开发一个[即时通讯应用](https://teamwork.com/chat)。另一方面来说,一个依赖于网络而没有任何与系统整合的桌面应用应该是一个 web 应用,并且仅仅是一个 web 应用。当用户并不能从桌面应用中获得比在浏览器中访问一个网址更多的价值的时候,期待用户下载你的应用(其中自带浏览器以及 Node.js)是不公平的。 + +> Instead of describing the desktop app you personally should build and why, I’m hoping to spark an idea or at least spark your interest in this article. Read on to see just how easy it is to create powerful desktop apps using web technology and what that can afford you over (or alongside of) creating a web app. + +比起描述你个人应该建造的桌面应用以及为什么建造,我更希望的是激发一个想法,或者只是激发你对这篇文章的兴趣。继续往下读来看看用 web 技术构造一个强大的桌面应用是多么简单,以及它将给予你的超过(或者相同)创造一个 web 应用。 ### NW.js -Desktop applications have been around a long time but you don’t have all day, so let’s skip some history and begin in Shanghai, 2011. Roger Wang, of Intel’s Open Source Technology Center, created node-webkit; a proof-of-concept Node.js module that allowed the user to spawn a WebKit browser window and use Node.js modules within ` +``` + +As you may realize, this URL will make the browser run the injected script and send the user’s cookies, potentially including confidential session cookies, to evil.com! + +To help protect users against reflective XSS attacks, some browsers have implemented protection mechanisms. These mechanisms try to identify these attacks by looking for matching code patterns in the HTTP request and response. Internet Explorer was the first browser to introduce such a mechanism with its XSS filter, introduced in Internet Explorer 8 back in 2008, and WebKit later introduced XSS Auditor, available today in Chrome and Safari. (Firefox has no similar mechanism built in, but users can use add-ons to gain this functionality.) These various protection mechanisms are not perfect: They may fail to detect a real XSS attack (a false negative), and in other cases may block legitimate code (a false positive). Due to the latter, browsers allow users to disable the XSS filter via the settings. Unfortunately, this is typically a global setting, which turns off this security feature completely for all web apps loaded by the browser. + +Luckily, there is a way for a web app to override this configuration and ensure that the XSS filter is turned on for the web app being loaded by the browser. This is done via the `X-XSS-Protection` header. This header, supported by Internet Explorer (from version 8), Edge, Chrome and Safari, instructs the browser to turn on or off the browser’s built-in protection mechanism and to override the browser’s local configuration. + +`X-XSS-Protection` directives include these: + +- `1` or `0` + +This enables or disables the filter. +- `mode=block` + +This instructs the browser to prevent the entire page from rendering when an XSS attack is detected. + +I recommend always turning on the XSS filter, as well as block mode, to maximize user protection. Such a response header looks like this: + +``` +X-XSS-Protection: 1; mode=block +``` + +Here’s how you would configure this response header in Node.js: + +``` +functionrequestHandler(req, res){ + res.setHeader('X-XSS-Protection','1;mode=block');} +``` + +### Controlling Framing ### + +An iframe (or HTML inline frame element, if you want to be more formal) is a DOM element that allows a web app to be nested within a parent web app. This powerful element enables some important web use cases, such as embedding third-party content into web apps, but it also has significant drawbacks, such as not being SEO-friendly and not playing nice with browser navigation — the list goes on. + +One of the caveats of iframes is that it makes clickjacking easier. Clickjacking is an attack that tricks the user into clicking something different than what they think they’re clicking. To understand a simple implementation of clickjacking, consider the HTML markup below, which tries to trick the user into buying a toaster when they think they are clicking to win a prize! + +``` + + + + + + +``` + +Clickjacking has many malicious applications, such as tricking the user into confirming a Facebook like, purchasing an item online and even submitting confidential information. Malicious web apps can leverage iframes for clickjacking by embedding a legitimate web app inside their malicious web app, rendering the iframe invisible with the `opacity: 0` CSS rule, and placing the iframe’s click target directly on top of an innocent-looking button rendered by the malicious web app. A user who clicks the innocent-looking button will trigger a click on the embedded web app — without at all knowing the effect of their click. + +An effective way to block this attack is by restricting your web app from being framed. `X-Frame-Options`, specified in [RFC 7034](https://www.ietf.org/rfc/rfc7034.txt), is designed to do exactly that! This header instructs the browser to apply limitations on whether your web app can be embedded within another web page, thus blocking a malicious web page from tricking users into invoking various transactions on your web app. You can either block framing completely using the `DENY` directive, whitelist specific domains using the `ALLOW-FROM` directive, or whitelist only the web app’s origin using the `SAMEORIGIN` directive. + +My recommendation is to use the `SAMEORIGIN` directive, which enables iframes to be leveraged for apps on the same domain — which may be useful at times — and which maintains security. This recommended header looks like this: + +``` +X-Frame-Options: SAMEORIGIN +``` + +Here’s an example of a configuration of this header to enable framing on the same origin in Node.js: + +``` +functionrequestHandler(req, res){ + res.setHeader('X-Frame-Options','SAMEORIGIN');} +``` + +### Explicitly Whitelisting Sources ### + +As we’ve noted earlier, you can add in-depth security to your web app by enabling the browser’s XSS filter. However, note that this mechanism is limited, is not supported by all browsers (Firefox, for instance, does not have an XSS filter) and relies on pattern-matching techniques that can be tricked. + +Another layer of in-depth protection against XSS and other attacks can be achieved by explicitly whitelisting trusted sources and operations — which is what Content Security Policy (CSP) enables web app developers to do. + +CSP is a [W3C specification](https://www.w3.org/TR/2016/WD-CSP3-20160901/) that defines a powerful browser-based security mechanism, enabling granular control over resource-loading and script execution in a web app. With CSP, you can whitelist specific domains for operations such as script-loading, AJAX calls, image-loading and style sheet-loading. You can enable or disable inline scripts or dynamic scripts (the notorious `eval`) and control framing by whitelisting specific domains for framing. Another cool feature of CSP is that it allows you to configure a real-time reporting target, so that you can monitor your app in real time for CSP blocking operations. + +This explicit whitelisting of resource loading and execution provides in-depth security that in many cases will fend off attacks. For example, by using CSP to disallow inline scripts, you can fend off many of the reflective XSS attack variants that rely on injecting inline scripts into the DOM. + +CSP is a relatively complex header, with a lot of directives, and I won’t go into the details of the various directives. HTML5 Rocks has a [great tutorial](https://www.html5rocks.com/en/tutorials/security/content-security-policy/) that provides an overview of CSP, and I highly recommend reading it and learning how to use CSP in your web app. + +Here’s a simple example of a CSP configuration to allow script-loading from the app’s origin only and to block dynamic script execution (`eval`) and inline scripts (as usual, on Node.js): + +``` +functionrequestHandler(req, res){ + res.setHeader('Content-Security-Policy',"script-src 'self'");} +``` + +### Preventing Content-Type Sniffing ### + +In an effort to make the user experience as seamless as possible, many browsers have implemented a feature called content-type sniffing, or MIME sniffing. This feature enables the browser to detect the type of a resource provided as part of an HTTP response by “sniffing” the actual resource bits, regardless of the resource type declared through the `Content-Type` response header. While this feature is indeed useful in some cases, it introduces a vulnerability and an attack vector known as a MIME confusion attack. A MIME-sniffing vulnerability enables an attacker to inject a malicious resource, such as a malicious executable script, masquerading as an innocent resource, such as an image. With MIME sniffing, the browser will ignore the declared image content type, and instead of rendering an image will execute the malicious script. + +Luckily, the `X-Content-Type-Options` response header mitigates this vulnerability! This header, introduced in Internet Explorer 8 back in 2008 and currently supported by most major browsers (Safari is the only major browser not to support it), instructs the browser not to use sniffing when handling fetched resources. Because `X-Content-Type-Options` was only formally specified as part of the [“Fetch” specification](https://fetch.spec.whatwg.org/#x-content-type-options-header), the actual implementation varies across browsers; some (Internet Explorer and Edge) completely avoid MIME sniffing, whereas others (Firefox) still MIME sniff but rather block executable resources (JavaScript and CSS) when an inconsistency between declared and actual types is detected. The latter is in line with the latest Fetch specification. + +`X-Content-Type-Options` is a simple response header, with only one directive: `nosniff`. This header looks like this: `X-Content-Type-Options: nosniff`. Here’s an example of a configuration of the header: + +``` +functionrequestHandler(req, res){ + res.setHeader('X-Content-Type-Options','nosniff');} +``` + +### Summary ### + +In this article, we have seen how to leverage HTTP headers to reinforce the security of your web app, to fend off attacks and to mitigate vulnerabilities. + +#### Takeaways #### + +- Disable caching for confidential information using the `Cache-Control` header. +- Enforce HTTPS using the `Strict-Transport-Security` header, and add your domain to Chrome’s preload list. +- Make your web app more robust against XSS by leveraging the `X-XSS-Protection` header. +- Block clickjacking using the `X-Frame-Options` header. +- Leverage `Content-Security-Policy` to whitelist specific sources and endpoints. +- Prevent MIME-sniffing attacks using the `X-Content-Type-Options` header. + +Remember that for the web to be truly awesome and engaging, it has to be secure. Leverage HTTP headers to build a more secure web! + + + +(**Disclaimer:** The content of this post is my own and doesn’t represent my past or current employers in any way whatsoever.) + +*Front page image credits: [Pexels.com](https://www.pexels.com/photo/coffee-writing-computer-blogging-34600/).* + + +--- + +> [掘金翻译计划](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)、[React](https://github.com/xitu/gold-miner#react)、[前端](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)。 From 70b8b484b9c34c825b0ca589e98fd852646ecb93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=B9=E5=8F=B7=E4=B8=89?= Date: Fri, 7 Apr 2017 20:54:36 +0800 Subject: [PATCH 058/638] Create webpack-and-rollup-the-same-but-different.md --- ...bpack-and-rollup-the-same-but-different.md | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 TODO/webpack-and-rollup-the-same-but-different.md diff --git a/TODO/webpack-and-rollup-the-same-but-different.md b/TODO/webpack-and-rollup-the-same-but-different.md new file mode 100644 index 00000000000..7bc3f470f12 --- /dev/null +++ b/TODO/webpack-and-rollup-the-same-but-different.md @@ -0,0 +1,58 @@ +> * 原文地址:[Webpack and Rollup: the same but different](https://medium.com/webpack/webpack-and-rollup-the-same-but-different-a41ad427058c) +> * 原文作者:[Rich Harris](https://medium.com/@Rich_Harris?source=post_header_lockup) +> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) +> * 译者: +> * 校对者: + +# Webpack and Rollup: the same but different # + +![](https://cdn-images-1.medium.com/max/1000/1*rtjClMZ8sq3cLFT9Aq8Xyg.png) + +This week, Facebook merged a [monster pull request](https://github.com/facebook/react/pull/9327) into React that replaced its existing build process with one based on [Rollup](https://rollupjs.org/) , [prompting](https://twitter.com/stanlemon/status/849366789825994752) [several](https://twitter.com/MrMohtas/status/849362334988595201) [people](https://twitter.com/kyleholzinger/status/849683292760797184) to ask ‘why did you choose Rollup over webpack’? + +Which is a completely reasonable question. [Webpack](https://webpack.js.org/) is one of the modern JavaScript community’s greatest success stories, with millions of downloads every month powering tens of thousands of websites and applications. It has a large ecosystem, dozens of contributors, and — unusually for a community open source project — [meaningful financial support](https://opencollective.com/webpack) . + +By comparison, Rollup is a minnow. But React isn’t alone — Vue, Ember, Preact, D3, Three.js, Moment, and dozens of other well-known libraries also use Rollup. So what’s going on? Why can’t we have just one JavaScript module bundler that everyone agrees on? + +### A tale of two bundlers ### + +webpack was started in 2012 by [Tobias Koppers](https://medium.com/@sokra) to solve a hard problem that existing tools didn’t address: building complex single-page applications (SPAs). Two features in particular changed everything: + +1. **Code-splitting** makes it possible to break your app apart into manageable chunks that can be loaded on-demand, meaning your users get an interactive site much faster than if they had to wait for the whole application to download and parse. You *can* do this manually, but, well… good luck. +2. **Static assets** such as images and CSS can be imported into your app and treated as just another node in the dependency graph. No more carefully placing your files in the right folders and hacked-together scripts for adding hashes to file URLs — webpack can take care of it for you. + +Rollup was created for a different reason: to build flat distributables of JavaScript libraries as efficiently as possible, taking advantage of the ingenious design of ES2015 modules. Other module bundlers — webpack included — work by wrapping each module in a function, putting them in a bundle with a browser-friendly implementation of `require`, and evaluating them one-by-one. That’s great if you need things like on-demand loading, but otherwise it’s a bit of a waste, and it [gets worse if you have lots of modules](https://nolanlawson.com/2016/08/15/the-cost-of-small-modules/). + +ES2015 modules enable a different approach, which Rollup uses. All your code is put in the same place and evaluates in one go, resulting in leaner, simpler code that starts up faster. You can [see it for yourself with the Rollup REPL](https://rollupjs.org/repl). + +But there’s a trade-off: code-splitting is a much hairier problem, and at the time of writing Rollup doesn’t support it. Similarly, Rollup doesn’t do hot module replacement (HMR). And perhaps the biggest pain point for people coming to Rollup — while it handles most CommonJS files (via a [plugin](https://github.com/rollup/rollup-plugin-commonjs) ), some things just don’t translate to ES2015, whereas webpack handles everything you throw at it with aplomb. + +### So which should I use? ### + +By now, hopefully it’s clear why both tools coexist and support each other — they serve different purposes. The tl;dr is this: + +> Use webpack for apps, and Rollup for libraries + +That’s not a hard and fast rule — lots of sites and apps are built with Rollup, and lots of libraries are built with webpack. But it’s a good rule of thumb. + +If you need code-splitting, or you have lots of static assets, or you’re building something with lots of CommonJS dependencies, Webpack is a better choice. If your codebase is ES2015 modules and you’re making something to be used by other people, you probably want Rollup. + +### Package authors: use `pkg.module!` ### + +For a long time, using JavaScript libraries was a bit of a crapshoot, because you and the library author effectively had to agree on a module system. If you were using Browserify but she preferred AMD, you would have to duct tape things together before you could actually build anything. The [Universal Module Definition](https://github.com/umdjs/umd) (UMD) format *sort of* fixed that, but because it wasn’t enforced anywhere you never knew quite what you were going to get. + +ES2015 changes all that, because `import` and `export` are part of the language. In the future, there’ll be no ambiguity, and things will work a lot more seamlessly. Unfortunately, because browsers (mostly) and Node don’t yet support `import` and `export`, we still need to ship UMD files (or CommonJS, if you’re building something Node-only). + +By adding a `"module": "dist/my-library.es.js"` entry to your library’s package.json file (aka `pkg.module`), it’s possible to serve UMD and ES2015 at the same time, right now. **That’s important because Webpack and Rollup can both use** `pkg.module` **to generate the most efficient code possible** — in some cases, they can even both [tree-shake](https://webpack.js.org/guides/tree-shaking/) unused parts of your library away. + +*Learn more about`pkg.module` on the [*Rollup wiki*](https://github.com/rollup/rollup/wiki/pkg.module) .* + +Hopefully this article makes the relationship between the two projects a bit clearer. If you still have questions, find us on Twitter at [rich_harris](https://twitter.com/rich_harris)/[rollupjs](https://twitter.com/rollupjs) and [thelarkinn](https://twitter.com/thelarkinn) . Happy bundling! + +Our thanks to Rich Harris for writing this article. We believe that collaboration in open source is incredibly vital to ensure we push technology and the web forward together. + +No time to help contribute? Want to give back in other ways? Become a Backer or Sponsor to webpack by [donating to our open collective](https://opencollective.com/webpack). Open Collective not only helps support the Core Team, but also supports contributors who have spent significant time improving our organization on their free time! ❤ + +--- + +> [掘金翻译计划](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)、[React](https://github.com/xitu/gold-miner#react)、[前端](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)。 From 0f630d62fc21721b39491c8886c93e1f933480ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=B9=E5=8F=B7=E4=B8=89?= Date: Fri, 7 Apr 2017 21:25:48 +0800 Subject: [PATCH 059/638] Create the-time-i-had-to-crack-my-own-reddit-password.md --- ...e-i-had-to-crack-my-own-reddit-password.md | 564 ++++++++++++++++++ 1 file changed, 564 insertions(+) create mode 100644 TODO/the-time-i-had-to-crack-my-own-reddit-password.md diff --git a/TODO/the-time-i-had-to-crack-my-own-reddit-password.md b/TODO/the-time-i-had-to-crack-my-own-reddit-password.md new file mode 100644 index 00000000000..a7cd6da89cd --- /dev/null +++ b/TODO/the-time-i-had-to-crack-my-own-reddit-password.md @@ -0,0 +1,564 @@ +> * 原文地址:[That time I had to crack my own Reddit password](https://medium.freecodecamp.com/the-time-i-had-to-crack-my-own-reddit-password-a6077c0a13b4) +> * 原文作者:[Haseeb Qureshi](https://medium.freecodecamp.com/@hosseeb?source=post_header_lockup) +> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) +> * 译者: +> * 校对者: + +# That time I had to crack my own Reddit password # + + + +Hack the planet, everybody. + +I have no self-control. + +Luckily, I know this about myself. This allows me to consciously engineer my life so that despite having the emotional maturity of a heroin-addicted lab rat, I’m occasionally able to get things done. + +![](https://media.giphy.com/media/gOH54eiriYIwM/giphy.gif) + + +Mm, a waste of time! + +I waste a lot of time on Reddit. If I want to procrastinate on something, I’ll often open a new tab and dive down a Reddit-hole. But sometimes you need to turn on the blinders and dial down distractions. 2015 was one of these times — I was singularly focused on improving as a programmer, and Redditing was becoming a liability. + +I needed an abstinence plan. + +So it occurred to me: how about I lock myself out of my account? + +**Here’s what I did:** + + + +I set a random password on my account. Then I asked a friend to e-mail me this password on a certain date. With that, I’d have a foolproof way to lock myself out of Reddit. (Also changed the e-mail for password recovery to cover all the bases.) + +This should have worked. + +Unfortunately it turns out, friends are very susceptible to social engineering. The technical terminology for this is that they are “nice to you” and will give you back your password if you “beg them.” + +![](https://media.giphy.com/media/uB6rsQFg5yPzW/giphy.gif) + +Don’t look at me like that. + +After a few rounds of this failure mode, I needed a more robust solution. A little Google searching, and I came across this: + + + + + +Looks legit. + +Perfect — an automated, friend-less solution! (I’d alienated most of them by now, so that was a big selling point.) + +A bit sketchy looking, but hey, any port in a storm. + + + +For a while I set this up this routine — during the week I’d e-mail myself my password, on the weekends I’d receive the password, load up on internet junk food, and then lock myself out again once the week began. It worked quite well from what I remember. + +Eventually I got so busy with programming stuff, I completely forgot about it. + +### **Cut to two years later.** ### + +I’m now gainfully employed at Airbnb. And Airbnb, it so happens, has a large test suite. This means waiting, and waiting of course means internet rabbit holes. + +I decide to scrounge up my old account and find my Reddit password. + + + +Oh no.That’s not good. + +I didn’t remember doing this, but I must have gotten so fed up with myself that** I locked myself out until 2018**. I also set it to “hide,” so I couldn’t view the contents of the e-mail until it’s sent. + +What do I do? Do I just have to create a new Reddit account and start from scratch? But that’s *so much work.* + +I could write in to LetterMeLater and explain that I didn’t mean to do this. But they would probably take a while to get back to me. We’ve already established I’m wildly impatient. Plus this site doesn’t look like it has a support team. Not to mention it would be an embarrassing e-mail exchange. I started brainstorming elaborate explanations involving dead relatives about why I needed access to the e-mail… + +All of my options were messy. I was walking home that night from the office pondering my predicament, when suddenly it hit me. + +**The search bar.** + +I pulled up the app on my mobile phone and tried it: + + + +Hmm. + +Okay. So it’s indexing the subject for sure. What about the body? + + + +I try a few letters, and voila. It’s definitely got the body indexed. Remember: the body consisted entirely of my password. + +*Essentially, I’ve been given an interface to perform substring queries.* By entering in a string into the search bar, the search results will confirm whether my password contains this substring. + +**We’re in business.** + +I hurry into my apartment, drop my bag, and pull out my laptop. + +Algorithms problem: you are given a function `substring?(str)`, which returns true or false depending on whether a password contains any given substring. *Given this function, write an algorithm that can deduce the hidden password.* + +### The Algorithm ### + +So let’s think about this. A few things I know about my password: I know it was a long string with some random characters, probably something along the lines of `asgoihej2409g`. I *probably* didn’t include any upper-case characters (and Reddit doesn’t enforce that as a password constraint) so let’s assume for now that I didn’t — in case I did, we can just expand the search space later if the initial algorithm fails. + +We also have a subject line as part of the string we’re querying. And we know the subject is “password”. + + + +Let’s pretend the body is 6 characters long. So we’ve got six slots of characters, some of which may appear in the subject line, some of which certainly don’t. So if we take all of the characters that aren’t in the subject and try searching for each of them, we know for sure we’ll hit a unique letter that’s in the password. Think like a game of Wheel of Fortune. + +![](https://cdn-images-1.medium.com/max/800/1*LOzh--_Ujutrh_OKhjfNaw.png) + +We keep trying letters one by one until we hit a match for something that’s not in our subject line. Say we hit it. + + + +Once I’ve found my first letter, I don’t actually know where in this string I am. But I know I can start building out a bigger substring by appending different characters to the end of this until I hit another substring match. + +We’ll potentially have to iterate through every character in our alphabet to find it. Any of those characters could be correct, so on average it’ll hit somewhere around the middle, so given an alphabet of size `A`, it should average out to `A/2` guesses per letter (let’s assume the subject is small and there are no repeating patterns of 2+ characters). + + + +I’ll keep building this substring until it eventually hits the end and no characters can extend it further. + + + +But that’s not enough — most likely, there will be a prefix to the string that I missed, because I started in a random place. Easy enough: all I have to do is now repeat the process, except going backwards. + + + +Once the process terminates, I should be able to reconstruct the password. In total, I’ll need to figure out`L` characters(where `L` is the length), and need to expend on average `A/2` guesses per character (where `A` is the alphabet size), so total guesses = `A/2 * L`. + +To be precise, I also have to add another `2A` to the number of guesses for ascertaining that the string has terminated on each end. So the total is `A/2 * L + 2A`, which we can factor as `A(L/2 + 2)`. + +Let’s assume we have 20 characters in our password, and an alphabet consisting of `a-z` (26) and `0–9` (10), so a total alphabet size of 36. So we’re looking at an average of `36 * (20/2 + 2) = 36 * 12 = 432` iterations. + +Damn. + +This is actually doable. + +![](https://media.giphy.com/media/119cVU19ICcAKc/giphy.gif) + +Programming IRL + +### The Implementation ### + +First things first: I need to write a client that can programmatically query the search box. This will serve as my substring oracle. Obviously this site has no API, so I’ll need to scrape the website directly. + +Looks like the URL format for searching is just a simple query string, `www.lettermelater.com/account.php?**qe=#{query_here}**`. That’s easy enough. + +Let’s start writing this script. I’m going to use the Faraday gem for making web requests, since it has a simple interface that I know well. + +I’ll start by making an API class. + +``` +require 'faraday' + +class Api + BASE_URL = 'http://www.lettermelater.com/account.php' + + def self.get(query) + Faraday.get(BASE_URL, qe: query) + end +end +``` + +Of course, we don’t expect this to work yet, as our script won’t be authenticated into any account. As we can see, the response returns a 302 redirect with an error message provided in the cookie. + +``` +[10] pry(main)> Api.get(“foo”) +=> #”Tue, 04 Apr 2017 15:35:07 GMT”, +“server”=>”Apache”, +“x-powered-by”=>”PHP/5.2.17", +“set-cookie”=>”msg_error=You+must+be+signed+in+to+see+this+page.”, +“location”=>”.?pg=account.php”, +“content-length”=>”0", +“connection”=>”close”, +“content-type”=>”text/html; charset=utf-8"}, +status=302> +``` + +So how do we sign in? We need to send in our [cookies](http://stackoverflow.com/questions/17769011/how-does-cookie-based-authentication-work) in the header, of course. Using Chrome inspector we can trivially grab them. + + + +(Not going to show my real cookie here, obviously. Interestingly, looks like it’s storing `user_id` client-side which is always a great sign.) + +Through process of elimination, I realize that it needs both `code` and `user_id` to authenticate me… sigh. + +So I add these to the script. (This is a fake cookie, just for illustration.) + +``` + +require 'faraday' + +class Api + BASE_URL = 'http://www.lettermelater.com/account.php' + COOKIE = 'code=13fa1fa011169ab29007fcad17b2ae; user_id=279789' + + def self.get(query) + Faraday.get(BASE_URL, { qe: query }, Cookie: COOKIE).body + end +end +``` + +``` +[29] pry(main)> Api.get(“foo”) +=> “\n\n\n\n\t\n\t\n\t\n\tLetterMeLater.com — Account Information… + +[30] pry(main)> _.include?(“Haseeb”) +=> true +``` + +It’s got my name in there, so we’re definitely logged in! + +We’ve got the scraping down, now we just have to parse the result. Luckily, this pretty easy — we know it’s a hit if the e-mail result shows up on the page, so we just need to look for any string that’s unique when the result is present. The string “password” appears nowhere else, so that will do just nicely. + + + +``` +def self.include?(substring) + get(substring).include?(‘password’) +end +``` + +That’s all we need for our API class. We can now do substring queries entirely in Ruby. + +``` +[31] pry(main)> Api.include?('password') +=> true +[32] pry(main)> Api.include?('f') +=> false +[33] pry(main)> Api.include?('g') +=> true +``` + +Now that we know that works, let’s stub out the API while we develop our algorithm. Making HTTP requests is going to be really slow and we might trigger some rate-limiting as we’re experimenting. If we assume our API is correct, once we get the rest of the algorithm working, everything should just work once we swap the real API back in. + +So here’s the stubbed API, with a random secret string: + +``` +class ApiStub + SECRET_PASSWORD = 'g420hpfjpefoj490rjgsd' + + def self.include?(substring) + SECRET_PASSWORD.include?(substring) + end +end +``` + +We’ll inject the stubbed API into the class while we’re testing. Then for the final run, we’ll use the real API to query for the real password. + +So let’s get started with this class. From a high level, recalling my algorithm diagram, it goes in three steps: + +1. First, find the first letter that’s not in the subject but exists in the password. This is our starting off point. +2. Build those letters forward until we fall off the end of the string. +3. Build that substring backwards until we hit the beginning of the string. + +Then we’re done! + +Let’s start with initialization. We’ll inject the API, and other than that we just need to initialize the current password chunk to be an empty string. + +``` +class PasswordCracker + def initialize(api) + @api = api + @password = '' + end +end +``` + +Now let’s write three methods, following the steps we outlined. + +``` + def crack! + find_starting_letter! + build_forward! + build_backward! + @password + end +``` + + +Perfect. Now the rest of the implementation can take place in private methods. + +For finding the first letter, we need to iterate over each character in the alphabet that’s not contained in the subject. To construct this alphabet, we’re going to use `a-z` and `0–9`. Ruby allows us to do this pretty easily with ranges: + +``` +ALPHABET = ((‘a’..’z’).to_a + (‘0’..’9').to_a).shuffle +``` + +I prefer to shuffle this to remove any bias in the password’s letter distribution. This will make our algorithm query A/2 times on average per character, even if the password is non-randomly distributed. + +We also want to set the subject as a constant: + +``` +SUBJECT = ‘password’ +``` + +That’s all the setup we need. Now time to write `find_starting_letter`. This needs to iterate through each candidate letter (in the alphabet but not in the subject) until it finds a match. + +``` + private + + def find_starting_letter! + candidate_letters = ALPHABET - SUBJECT.chars + @password = candidate_letters.find { |char| @api.include?(char) } + end +``` + +In testing, looks like this works perfectly: + +``` +PasswordCracker.new(ApiStub).send(:find_starting_letter!) # => 'f' +``` + +Now for the heavy lifting. + +I’m going to do this recursively, because it makes the structure very elegant. + +``` +def build_forward! + puts "Current password: #{@password}" + ALPHABET.each do |char| + guess = @password + char + + if @api.include?(guess) + @password = guess + build_forward! + # once I'm done building forward, jump out of all stack frames + return + end + end + end +``` + +The code is surprisingly straightforward. Let’s see if it works with our stub API. + +``` +[63] pry(main)> PasswordCracker.new(ApiStub).crack! +f +fj +fjp +fjpe +fjpef +fjpefo +fjpefoj +fjpefoj4 +fjpefoj49 +fjpefoj490 +fjpefoj490r +fjpefoj490rj +fjpefoj490rjg +fjpefoj490rjgs +fjpefoj490rjgsd +=> “fjpefoj490rjgsd” +``` + +Awesome. We’ve got a suffix, now just to build backward and complete the string. This should look very similar. + +``` +def build_backward! + puts "Current password: #{@password}" + ALPHABET.each do |char| + guess = char + @password + + if @api.include?(guess) + @password = guess + build_backward! + return + end + end +``` + + +In fact, there’s only two lines of difference here: how we construct the `guess`, and the name of the recursive call. There’s an obvious refactoring here, so let’s do it. + +``` +def build!(forward:) + puts "Current password: #{@password}" + ALPHABET.each do |char| + guess = forward ? @password + char : char + @password + + if @api.include?(guess) + @password = guess + build!(forward: forward) + return + end + end + end +``` + +Now these other calls simply reduce to: + +``` + def build_forward! + build!(forward: true) + end + + def build_backward! + build!(forward: false) + end +``` + +And let’s see how it works in action: + + +``` +Apps-MacBook:password-recovery haseeb$ ruby letter_me_now.rb +Current password: 9 +Current password: 90 +Current password: 90r +Current password: 90rj +Current password: 90rjg +Current password: 90rjgs +Current password: 90rjgsd +Current password: 90rjgsd +Current password: 490rjgsd +Current password: j490rjgsd +Current password: oj490rjgsd +Current password: foj490rjgsd +Current password: efoj490rjgsd +Current password: pefoj490rjgsd +Current password: jpefoj490rjgsd +Current password: fjpefoj490rjgsd +Current password: pfjpefoj490rjgsd +Current password: hpfjpefoj490rjgsd +Current password: 0hpfjpefoj490rjgsd +Current password: 20hpfjpefoj490rjgsd +Current password: 420hpfjpefoj490rjgsd +Current password: g420hpfjpefoj490rjgsd +g420hpfjpefoj490rjgsd +``` + +Beautiful. Now let’s just add some more print statements and a bit of extra logging, and we’ll have our finished `PasswordCracker`. + +``` +require 'faraday' + +class PasswordCracker + ALPHABET = (('a'..'z').to_a + ('0'..'9').to_a).shuffle + SUBJECT = 'password' + + def initialize(api) + @api = api + @password = '' + end + + def crack! + find_starting_letter! + puts "Found first letter: #{@password}" + puts "\nBuilding forward!\n" + build_forward! + puts "\nBuilding backward!\n" + build_backward! + puts "Done! The result is #{@password}." + puts "We found it in #{@api.iterations} iterations" + @password + end + + private + + def find_starting_letter! + candidate_letters = ALPHABET - SUBJECT.chars + @password = candidate_letters.find { |char| @api.include?(char) } + end + + def build_forward! + build!(forward: true) + end + + def build_backward! + build!(forward: false) + end + + def build!(forward:) + puts "Current password: #{@password}" + ALPHABET.each do |char| + guess = forward ? @password + char : char + @password + + if @api.include?(guess) + @password = guess + build!(forward: forward) + return + end + end + end +end + +class Api + BASE_URL = 'http://www.lettermelater.com/account.php' + COOKIE = 'code=13fa1fa011169ab29007fcad17b2ae; user_id=279789' + @iterations = 0 + + def self.get(query) + @iterations += 1 + Faraday.get(BASE_URL, { qe: query }, Cookie: COOKIE).body + end + + def self.include?(substring) + get(substring).include?('password') + end + + def self.iterations + @iterations + end +end + +class ApiStub + SECRET_PASSWORD = 'g420hpfjpefoj490rjgsd' + @iterations = 0 + + def self.include?(substring) + @iterations += 1 + SECRET_PASSWORD.include?(substring) + end + + def self.iterations + @iterations + end +end +``` + +And now… the magic moment. Let’s swap the stub with the real API and see what happens. + +### The Moment of Truth ### + +Cross your fingers… + +`PasswordCracker.new(Api).crack!` + + + +(Sped up 3x) + +Boom. 443 iterations. + +Tried it out on Reddit, and login was successful. + +Wow. + +It… actually worked. + +Recall our original formula for the number of iterations: `A(N/2 + 2)`. The true password was 22 characters, so our formula would estimate `36 * (22/2 + 2) = 36 * 13 = 468` iterations. Our real password took 443 iterations, so our estimate was within 5% of the observed runtime. + +**Math.** + +![](https://media.giphy.com/media/26xBI73gWquCBBCDe/giphy.gif) + +tfw wtf ftw + +**It works.** + +Embarrassing support e-mail averted. Reddit rabbit-holing restored. It’s now confirmed: programming is, indeed, magic. + +(The downside is I am now going to have to find a new technique to lock myself out of my accounts.) + +And with that, I’m gonna get back to my internet rabbit-holes. Thanks for reading, and give it a like if you enjoyed this! + +*—Haseeb* +--- + +> [掘金翻译计划](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)、[React](https://github.com/xitu/gold-miner#react)、[前端](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)。 From 8b1ccb184fbd2898c18da673889fa2466202e965 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=B9=E5=8F=B7=E4=B8=89?= Date: Fri, 7 Apr 2017 21:48:45 +0800 Subject: [PATCH 060/638] Create creating-usability-with-motion-the-ux-in-motion-manifesto.md --- ...-with-motion-the-ux-in-motion-manifesto.md | 452 ++++++++++++++++++ 1 file changed, 452 insertions(+) create mode 100644 TODO/creating-usability-with-motion-the-ux-in-motion-manifesto.md diff --git a/TODO/creating-usability-with-motion-the-ux-in-motion-manifesto.md b/TODO/creating-usability-with-motion-the-ux-in-motion-manifesto.md new file mode 100644 index 00000000000..b7982b9da14 --- /dev/null +++ b/TODO/creating-usability-with-motion-the-ux-in-motion-manifesto.md @@ -0,0 +1,452 @@ +> * 原文地址:[Creating Usability with Motion: The UX in Motion Manifesto](https://medium.com/@ux_in_motion/creating-usability-with-motion-the-ux-in-motion-manifesto-a87a4584ddc) +> * 原文作者:[Issara Willenskomer](https://medium.com/@ux_in_motion?source=post_header_lockup) +> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) +> * 译者: +> * 校对者: + + + +# Creating Usability with Motion: The UX in Motion Manifesto # + +The following manifesto represents my answer to the question — “As a UX or UI, designer, how do I know when and where to implement motion to support usability?” + +Over the last 5 years, it has been my privilege to coach and mentor UX & UI designers in over 40 countries, and at hundreds of the top brands and design consultancies through my workshops and tutorials on UI Animation. + +After over fifteen years studying motion in user interfaces, I have come to the conclusion that there are 12 specific opportunities to support usability in your UX projects using motion. + +I call these opportunities ‘The 12 Principles of UX in Motion,’ and they can be stacked and combined synergistically in a myriad of innovative ways. + +I’ve broken the manifesto into 5 parts: + +1. Addressing the topic of UI Animation — it’s not what you think +2. Realtime vs non-realtime interactions +3. Four ways that motion supports usability +4. Principles, Techniques, Properties and Values +5. The 12 Principles of UX in Motion + +As a quick plug, if you want me to speak at your conference or lead an onsite workshop for your team on the exciting topic of motion and usability, go [here](https://uxinmotion.net/workshops-and-speaking/) . If you want to attend a class in your city go [here](https://uxinmotion.net/workshops-and-speaking/#classes) . Finally, if you want me to consult on your project, you can go [here](https://uxinmotion.net/consulting/) . To get added to my list, go [here](http://uxinmotion.net/joinnow) + +### It’s not about UI Animation ### + +Because the topic of motion in user interfaces is mostly understood by designers to be ‘UI Animation’—which it is not — I feel like I need to create a bit of context before we jump into the 12 Principles. + +‘UI Animation’ is typically thought of by designers as something that makes the user experience more delightful, but overall doesn’t add much value. Thus, UI Animation is often treated like the red-headed stepchild of UX (apologies to red-headed stepchildren everywhere). If at all, it usually comes at the end, as a final lipstick pass. + +Additionally, motion in the context of user interfaces has understandably been held to be under the domain of Disney’s 12 Principles of Animation, something I argue against in my article ‘[UI Animation Principles — Disney is Dead](https://medium.com/@ux_in_motion/ui-animation-principles-disney-is-dead-8bf6c66207f9) .’ + +In my manifesto, I will be making the case that UI Animation is to the ‘12 UX in Motion Principles’ as construction is to architecture. + +By this I mean, that while a structure needs to be physically built to exist (requiring construction), the guiding hand which determines *what* gets built comes from the domain of Principles. + +Animation is all about tools. Principles are the practical application of ideas that guide the usage of tools and as such, Principles provide high leverage opportunities for designers. + +What most designers think of as ‘UI Animation’ is in fact the execution of a higher modality of design: the temporal behavior of interface objects during realtime and non-realtime events. + + +### Realtime vs non-realtime interactions ### + +At this juncture, it is important to distinguish between ‘state’ and ‘act.’ The *state* of something in UX is fundamentally static, like a design comp. The *act *of something in UX is fundamentally temporal, and motion based. An object can be in the *state *of being masked or it can be in the *act* of being masked. If it is the latter, we know that motion is involved and in a way that could support usability. + +Additionally, all temporal aspects of interaction can be thought of as happening in realtime or non-realtime. Realtime means that the user is directly interacting with the objects in the user interface. Non-realtime means that the object behavior is post-interactive: it occurs *after *a user action, and is transitional. + + + + + +This is an important distinction. + +Realtime interactions can also be though of as ‘direct manipulation,’ in that the user is interacting with the interface objects directly and *immediately*. The behavior of the interface is happening *as the user is using it.* + +Non-realtime interactions happen only *after *input from the user and have the effect of briefly locking the user out of the user experience until the transition completes. + +These distinctions will give us leverage as we continue our journey into the world of the 12 Principles of UX in Motion. + + +### Motion supports usability in four ways ### + +These four pillars represent the four ways where the temporal behavior of user experiences supports usability. + +#### Expectation #### + +Expectation fall into two areas — how users perceive what an object *is*, and how it *behaves*. Another way of saying this is that as designers, we want to minimize the gap between what the user expects, and what they experience. + +#### Continuity #### + +Continuity speaks both to the user flow and to the ‘consistency’ of the user experience. Continuity can be thought of in terms of ‘intra-continuity’ — the continuity within a scene, and ‘inter-continuity’ — the continuity within a series of scenes that make up the total user experience. + +#### Narrative #### + +Narrative is the linear progression of events in the user experience that results in a temporal/spatial framework. This can be thought of as the series of discreet moments and events that connect together throughout the user experience. + +#### Relationship #### + +Relationship refers to the spatial, temporal, and hierarchal representations between interface objects that guide user understanding and decision making. + +### Principles, Techniques, Properties, and Values ### + +[Tyler Waye](http://tylerwaye.com/learning-to-learn-principles-vs-techniques/) says it as good as any when he writes, “Principles… are the underlying premises and rules of function giving rise to any number of techniques. These elements remain consistent, no matter what is happening.” It bears repeating that Principles are agnostic of design. + +From there, we can imagine a hierarchy with Principles at the top, Techniques further down, Properties below that, and Values at the bottom. + +**Techniques** can be thought of as the various and unlimited executions of Principles and/or combination of Principles. I think of technique as akin to ‘style.’ + +**Properties** are the specific object parameters that are being animated to create the technique. These include (and are not limited to) position, opacity, scale, rotation, anchor point, color, stroke-width, shape, etc. + +**Values** are the actual numeric property values that vary over time to create what we call ‘animation.’ + +So to land the plane here (and jumping ahead a bit), we could say that a hypothetical UI animation reference is using the Obscuration Principle with a ‘blurred glass’ Technique that affects the Blur and Opacity Properties at a Value of 25px and 70% respectively. + +Now we have some tools to work with. And more importantly, these linguistic tools are agnostic of any specific prototyping tool. + + +### The 12 Principles of UX in Motion ### + +Easing and Offset & Delay relate to *timing*. Parenting relates to *object relationship*. Transformation, Value Change, Masking, Overlay, and Cloning all relate to *object continuity*. Parallax relate to *temporal hierarchy*. Obscuration, Dimensionality and Dolly & Zoom both relate to *spatial continuity*. + + + +#### **Principle 1: Easing** #### + +*Object behavior aligns with user expectations when temporal events occur.* + + + +All interface objects exhibiting temporal behavior (whether realtime or non-realtime), ease. Easing creates and reinforces the ‘naturalism’ inherent in the seamlessness of user experiences, and creates a sense of continuity when objects behave *as users expect them to. *Incidentally*, *Disney refers to this as ‘[Slow In and Slow Out](https://en.wikipedia.org/wiki/12_basic_principles_of_animation#Slow_In_and_Slow_Out).’ + + + + + +The example on the left has linear motion and looks ‘bad.’ The first example up top has *eased* motion and looks ‘good.’ All three above examples have the exact number of frames and take place over the exact same amount of time. The only difference is in their easing. + +As designers concerned with usability, we need to require of ourselves rigor and inquire, aside from aesthetics, which example supports usability more? + +The case I am presenting here is that a certain degree of skeuomorphism is inherent in easing. You can imagine an ‘easing gradient’ wherein behaviors that fall outside user expectations result in less usable interactions. In the case of properly eased motion, users experience the motion itself as seamless and largely invisible — which is a good thing in that it is *non-distracting*. Linear motion is visibly obvious and feels somehow off, unfinished, and jarring, and distracting. + +Now I’m going to completely contradict myself here and talk about the example on the right. The motion *isn’t* seamless. In fact, it has a ‘designed’ feel to it. We notice how the object lands. It feels different, yet it still feels more ‘correct’ than the example with linear motion. + +Can you employ easing and still have it not support (or worse case undermine) usability? The answer is yes, and there are several ways. One way is timing. If your timing is too slow (mushy to borrow from [Pasquele](https://medium.com/@pasql) ), or too fast, you can break expectation and distract user attention. Similarly, if your easing is misaligned with the brand or overall experience, this can also negatively impact expectation and seamlessness. + +What I want to open you to is a world of opportunity when it comes to eased motion. There are literally an infinite number of ‘easings’ that you as a designer can create and implement in your projects. All of these easings have their own expectation response they trigger in users. + +To summarize: when to use easing? Always. + +#### Principle 2: Offset & Delay #### + +*Defines object relationships and hierarchies when introducing new elements and scenes.* + + + +Offset & Delay is the second of only two UX in Motion Principles that is influenced by Disney’s Animation Principles, in this case from ‘[Follow Through and Overlapping Action](https://en.wikipedia.org/wiki/12_basic_principles_of_animation#Follow_Through_and_Overlapping_Action).’ + +It is important to note, however, that the implementation, while similar in execution, differs in purpose and outcome. While Disney’s Principles result in ‘more appealing animations,’ the UI Animation Principles result in more usable experiences. + +The utility of this principle is that it pre-consciously sets the user up for success by ‘telling’ the user something about the nature of the objects in the interface. The narrative in the above reference is that the top two objects are united and the bottom object is separate. Perhaps the top two objects could be a non-interactive image and text, while the bottom object is a button. + +Even before the user registers what these objects *are, *the designer has already communicated to her—through motion — that the objects are somehow ‘separate.’ This is extremely powerful. + + + +Credit: [InVision](https://dribbble.com/InVisionApp) + +In the above example, the floating action button (FAB) transforms into header navigation elements comprised of three buttons. Because the buttons are ‘offset’ from each other temporally, they end up supporting usability through their ‘separateness.’ Said differently, the designer is using time itself to say — even before the user registers what the objects are — that the objects are separate. This has the effect of telling the user, completely independent from the visual design, part of the nature of the objects in the interface. + +To better show you how this works, I’ll show you an example that breaks the Offset & Delay Principle. + + + +Credit: [Jordi Verdu](https://dribbble.com/jordiverdu) ( + +In the above example, the static visual design tells us that there are icons over a background. The presumption is that the icons are all separate from each other and do different things. However, the motion contradicts this. + +Temporally, the icons are grouped into rows and behave like a single object. Their titles are likewise grouped into rows, and also behave like a single object. The motion is telling the user something other than what her eyes see. In this case, we can say that the temporal behavior of the objects in the interface are not supporting usability. + +#### Principle 3: Parenting #### + +*Creates spatial and temporal hierarchal relationships when interacting with multiple objects.* + + + +Parenting is a powerful Principle that ‘relates’ objects in the user interface. In the above example, the ‘scale’ and ‘position’ property of the top or ‘child’ object is parented to the ‘position’ property of the bottom or ‘parent’ object. + +Parenting is the linking of object properties to other object properties. This creates object relationships and hierarchies in ways that support usability. + +Parenting also allows designers to better coordinate temporal events in the user interface, while communicating to users the nature of the object relationships. + +Recall that object Properties include the following — Scale, Opacity, Position, Rotation, Shape, Color, Value, etc. Any of these Properties can be linked to any other Properties to create synergistic moments in the User Experience. + + + + + +Credit: [Andrew J Lee](https://dribbble.com/lee_aj) , [Frank Rapacciuolo](https://dribbble.com/frankiefreesbie) + +In this above left example, the ‘y-axis’ Property of the ‘face’ element is the ‘child’ to the ‘x-axis’ Property of the round indicator parent. When the round indicator element moves along the horizontal axis, its ‘child’ element moves along with it horizontally *and* vertically (while being Masked — another Principle). + +The result is a hierarchal temporal narrative framework that occurs all at the same time. Of note is that the ‘faces’ object works as a series of ‘lockups’ in that, at each number, the ‘face’ is fully and not partially visible. The user experiences this seamlessly, though we can make the claim there is a subtle ‘usability cheat’ in this example. + +Parenting functions best as a ‘realtime’ interaction. As the user directly manipulates the interface objects, the designer communicates to the user — via motion — how the objects are linked, and the relationship between them. + +Parenting occurs in 3 forms: ‘direct parenting’ (see above two examples, ‘delayed parenting,’ and ‘inverse parenting,’ see below). + + + + + +Delayed Parenting (Credit: [AgenceMe](https://dribbble.com/AgenceMe) ) & Inverse Parenting (Credit: [AgenceMe](https://dribbble.com/AgenceMe) ) + +#### Principle 4: Transformation #### + +*Creates a continuous state of narrative flow when object utility changes.* + + + +Much has already been written about the UX in Motion Principle ‘transformation.’ In some ways, it is the most obvious and penetrable of the animation principles. + +Transformation is the most discernible, largely because it stands out. A ‘submit’ button changing shape to become a radial progress bar and finally changing shape again to become a confirmation check mark is something that we notice. It grabs our attention, tells a story, and has completion. + + + +Credit: [Colin Garven](https://dribbble.com/ColinGarven) + +What Transformation does is seamlessly transition the user through the different UX states or ‘is’s’ (as in this *is* a button, this *is* a radial progress bar, this *is* a check mark) which eventually result in a desired outcome. The user has been drawn through these functional spaces to the ultimate destination. + +Transformation has the effect of ‘chunking’ cognitively separate key moments in the user experience into a seamless and continuous series of events. This seamlessness results in better user awareness, retention, and followthrough. + +#### Principle 5: Value change #### +*Creates a dynamic and continuous narrative relationship when value subject changes.* + + + +Text based interface objects, that is to say numbers and text, can change their values. This is one of those ‘elusive obvious’ concepts. + +Text and number changes are so common that they pass us by without us bringing to them distinction and rigor to assess their role in supporting usability + +So what is the user experiencing when values change? In user experiences, the 12 UX in Motion Principles are opportunities to support usability. The three opportunities here are to connect the user to the *reality* behind the data, the concept of *agency,* and to the dynamic nature of the values themselves. + +Let’s look at the example of a user dashboard. + + + + + +When value based interface objects load with no ‘value change,’ what this conveys to the user is that the numbers are static objects. They’re like painted signs displaying a speed limit of 55 mph. + +The numbers and values are representations of things that are happening *in reality.* That reality could be time, income, game scores, business metrics, fitness tracking, etc. What we are distinguishing through motion is that the ‘value subject’ is dynamic and the values are reflecting something from that dynamic value set. + +Not only does this relationship get lost with static objects comprised visually of values, but another deeper opportunity is also lost. + +When we employ representations of dynamic systems in the form of motion based values, it activates a sort of ‘neurofeedback.’ Users, grasping the dynamic nature of their data can now be cause in altering those values and are empowered to become *agents*. When the values are static, there is less connection to the reality behind the values, and users lose their *agency*. + + + + + + + +Credit: [Barthelemy Chalvet](https://dribbble.com/BarthelemyChalvet), [Gal Shir](https://dribbble.com/galshir) , Unknown + +The Value Change Principle can occur both in realtime and non-realtime events. In realtime events, the user is interacting with the objects to change values. In non-realtime events, such as loaders and transitions, the values change without user input to reflect a dynamic narrative. + +#### Principle 6: Masking #### + +*Creates continuity in an interface object or object group when utility is determined by which part of the object or group is revealed or concealed.* + + + +The *act* of masking asking can be thought of as a relationship between the shape of the object and it’s utility. + +Because designers are familiar with ‘masking’ in the context of static design, it behooves us to bring distinction to the UX in Motion Principle ‘Masking’ as it occurs in time, *as an* act, and not as a *state*. + +Through the temporal use of revealing and concealing regions of an object, utility transitions in a continuous and seamless way. This also has the effect of preserving narrative flow. + + + +Credit: [Anish Chandran](https://dribbble.com/anish_chandran) + +In the above example, the header image changes bounding shape and position but not the content, and becomes an album. This has the effect of changing what the object *is,while preserving the content within the mask — a fairly neat trick*. *This occurs in non-realtime, as a transition, that is activated after a user takes an action. + +Remember, UI Animation Principles occur temporally and support usability through continuity, narrative, relationship, and expectation. In the above reference, while the object itself remains unchanged, it also has boundary and location, and these two factors determine what the object is. + +#### Principle 7: Overlay #### + +*Creates narrative and object spatial relationship in visual flatland when layered objects are location dependent.* + + + +Overlay supports usability by allowing users to utilize flatland ordering properties to overcome a lack of non-spatial hierarchies. + +To land the plane a bit, Overlay allows designers to use motion to communicate location dependent objects that exist behind or in front of others in non 3D space. + + + + + +Credit: [Bady](https://dribbble.com/bady), [Javi Pérez](https://dribbble.com/javiperez) + +In the example on the left, the foreground object slides to the right to reveal the location of additional background objects. In the example on the right, the entire scene slides down to reveal additional content and options (while using the Offset & Delay Principle to communicate the individuality of the photo objects). + +To a certain degree, as designers, the idea of ‘layers’ is so obvious as to be self-evident. We design with layers and the concept of layers are deeply internalized. However, we must be careful to distinguish between the process of ‘making’ verses ‘using.’ + +As designers who are continually engaged in the process of ‘making,’ we are intimately familiar with all of the pieces of the object (including the hidden pieces) we are designing. As a user, however, those non visible pieces are by definition and practice, hidden both visually and cognitively. + +The Overlay Principle allows designers to communicate relationship between ‘z-axis’ positioned layers and in so doing, promote spatial orientation to their users. + +#### Principle 8: Cloning #### + +*Creates continuity, relationship and narrative, when new objects originate and depart.* + + + +When new objects are created in current scenes (and from current objects), it is important to narratively account for their appearance. In this manifesto, I argue forcefully for the importance of creating a narrative framework for object origin and departure. Simple opacity fades tend to not achieve this result. Masking, Cloning, and Dimensionality are three usability based approaches to produce strong narratives. + + + + + + + +Credit: [Jakub Antalík](https://dribbble.com/antalik) , [Jakub Antalík](https://dribbble.com/antalik) , Unknown + +In the above three examples, new objects are created from existing hero objects during the time the user’s attention is focused on those objects. This two fold approach — the directing of attention, and then leading the eye through the creation of a cloned new object — has the strong effect of communicating a clear and unambiguous chain of events: that action ‘x’ has the result ‘y’ of the creation of new child objects. + +#### Principle 9: Obscuration #### + +*Allows users to spatially orient themselves in relationship to objects or scenes not in the primary visual hierarchy.* + + + +Similar to the UX in Motion Principles of Masking, Obscuration lives as both a static and temporal phenomena. + +This can be confusing to designers who don’t have experience thinking temporally — that is, the moments *between* moments. Designers typically design screen to screen or task to task. Think of Obscuration as the *act* of being obscured and not the *state* of being obscured. A static design represents the state of being obscured. Introducing time gives us the *act* of an object being obscured. + + + + + +Credit: [Virgil Pana](https://dribbble.com/virgilpana), [Apple](http://www.apple.com/) + +From the above two examples, we can see that obscuration, which *looks like* transparent objects or overlays, is also a temporal interaction involving multiple properties in time. + +Various common techniques of this involve blur effects and a lessoning of overall object transparency. The user is made aware of an additional non primary context that she is operating in — that there is another world, as it were, ‘behind’ her primary object hierarchy. + +Obscuration allows designers to compensate for a single unified field of view, or ‘objective view,’ in user experiences. + +#### Principle 10: Parallax #### + +*Creates spatial hierarchy in visual flatland when users scroll.* + + + +‘Parallax,’ as a UX in Motion Principle describes different interface objects moving at different rates. + +Parallax allows user to focus on primary actions and content while maintaining design integrity. Background elements ‘recede’ perceptually and cognitively for the user during a Parallax event. Designers can use Parallax to separate out immediacy content from ambient or supportive content. + + + + + +Credit: [Austin Neill](https://dribbble.com/austinneill), [Michael Sevilla](https://dribbble.com/SVLA) + +The effect this has on the user, is to clearly define *for the duration of the interaction,* the various object relationships. Foreground objects, or objects that move ‘quicker’ appear to the user as ‘closer.’ Likewise, background objects or objects that move ‘slower’ are perceived as being ‘further away.’ + +Designers can create these relationships, using time itself, to tell the user what objects in the interface are higher priority. Therefore it makes sense to push background or non-interactive elements further ‘back.’ + +Not only does the user perceive the interface objects as now having a hierarchy beyond that which is determined in the visual design, but this hierarchy can now be leveraged into having the user grasp *the nature* of the user experience before being consciously aware of the design/content. + +#### Principle 11: Dimensionality #### + +*Provides a spatial narrative framework when new objects originate and depart.* + + + +Critical to User Experiences is the phenomenon of continuity as well as the sense of location. + +Dimensionality provides a powerful way to overcome the flatland non-logic of User Experiences. + +Humans are remarkably adept at using spatial frameworks to navigate both in the real world and in digital experiences. Providing spatial origin and departure references helps reinforce mental models of where users are in the UX. + +Additionally, the Dimensionality Principle overcomes the layering paradox in visual flatland wherein objects lacking depth exist on the same plane but occur as ‘in front of’ or ‘behind’ other objects. + +Dimensionality presents itself in three ways — Origami Dimensionality, Floating Dimensionality, and Object Dimensionality. + +**Origami Dimensionality** can be thought of in terms of ‘folding’ or ‘hinged’ three dimensional interface objects. + + + + + +Examples of Origami Dimensionality (Credit: [Eddie Lobanovskiy](https://dribbble.com/lobanovskiy) , [Virgil Pana](https://dribbble.com/virgilpana)) + +Because multiple objects are combined into ‘origami’ structures, the hidden objects still can be said to ‘exist,’ spatially even though they are not visible. This effectively renders the User Experience as a continuous spatial event that the user navigates and creates an operating context both in the interaction model itself, and in the temporal behavior of the interface objects themselves. + +**Floating Dimensionality** gives interface objects a spatial origin and departure, making the interaction models intuitive and highly narrative. + + + +Example of Floating Dimensionality (Credit: [Virgil Pana](https://dribbble.com/virgilpana) ) + +In the above example, Dimensionality is achieved through the use of 3D ‘cards.’ This provides a strong narrative framework that supports the visual design. The narrative is extended by ‘flipping’ the cards to access additional content and interactivity. Dimensionality can be a powerful way to introduce new elements in ways that minimize abruptness. + +**Object Dimensionality **results in dimensional objects with true depth and form. + + + + + +Examples of Object Dimensionality (Credit: [Issara Willenskomer](https://uxinmotion.net/) , [Creativedash](https://dribbble.com/Creativedash) ) + +Here, multiple 2D layers are arranged in 3D space to form true dimensional objects. Their dimensionality is revealed during realtime and non-realtime transitional moments. The utility of Object Dimensionality is that users develop a keen awareness of object utility based on non-visible spatial locations. + +#### Principle 12: Dolly & Zoom #### + +*Preserves continuity and spatial narrative when navigating interface objects and spaces.* + + + +Dolly and Zoom are filmic concepts referring to the movement of objects relevant to the camera, and the size of the image itself in the frame smoothly changing from a long shot to a close up shot (or vice versa). + +In certain contexts, it is impossible to tell if an object is zooming, if it is moving towards the camera in 3D space, or if the camera is moving towards the object in 3D space (see below references). The below three examples illustrate the possible scenarios. + +![](https://cdn-images-1.medium.com/max/800/1*R9wPWQUu26wjibaTBUstqQ.gif) + +Is the layer dollying, zooming, or is the camera moving? +As such, it is appropriate to treat the instances of ‘dollying’ and ‘zooming’ as separate but similar in that they involve continuous element and scenic transformations, and meet the requirements of the UX in Motion Principles: they support usability through motion. + +![](https://cdn-images-1.medium.com/max/400/1*I4yZ2k1zeo3qc9qrbn0LDw.gif) + +![](https://cdn-images-1.medium.com/max/400/1*XVtnYMrp8LhGJzcsF0Lw7Q.gif) + +![](https://cdn-images-1.medium.com/max/400/1*o2ellGNN8CTJbwUoJ0ts8Q.gif) + +The two images on the left are dollying, while the image on the right is zooming + +**Dolly** is a film term and applies to camera movement either toward or away from a subject (it also applies to horizontal ‘tracking’ movement as well, but is less relevant in a usability context). + +![](https://cdn-images-1.medium.com/max/800/1*8TYALn5P87i2OuuZfhfELg.gif) + +Credit: [Apple](http://www.apple.com/) + +Spatially in UX, this motion could refer either to a change in the viewers perspective, or to the perspective remaining static while the object changes position. The Dolly Principle supports usability through continuity and narrative, seamlessly transitioning between interface objects and destinations. Dolly can also incorporate the Dimensionality Principle, resulting in a more spatial experience, more depth, and communicating to the user additional areas or content that is ‘in front’ or ‘behind’ the current view. + +**Zoom** refers to events where neither the perspective nor the object is moving spatially, but rather the object itself is scaling (or our view of it is decreasing, resulting in the image enlarging). This communicates to the viewer that additional interface objects are ‘inside’ other objects or scenes. + +![](https://cdn-images-1.medium.com/max/800/1*I6-dXGCq9cXjAZGyVOkXrA.gif) + +Credit: [Apple](http://www.apple.com/) + +This allows for seamless transitions — both realtime and non-realtime — that support usability. This seamlessness employed in the Dolly & Zoom Principle is quite powerful when it comes to creating spatial mental models. + +If you made it this far, congratulations! That was a beast of a manifesto. I hope all the gifs loaded for you and didn’t kill your browser. I also really hope you got some value for yourself and some new tools and leverage in your interactive projects. + +I encourage you to learn more about how you can begin using motion as a design tool to support usability. + +Again, the final plug, if you want me to speak at your conference or lead an onsite workshop for your team on the exciting topic of motion and usability, go [here](https://uxinmotion.net/workshops-and-speaking/) . If you want to attend a class in your city go [here](https://uxinmotion.net/workshops-and-speaking/#classes) . Finally, if you want me to consult on your project, you can go [here](https://uxinmotion.net/consulting/) . To get added to my list, go [here](http://uxinmotion.net/joinnow) . + +This manifesto would not have been possible without the generous and patient contribution and constant feedback of [Kateryna Sitner](https://www.linkedin.com/in/katerynasitner/) from Amazon — thank you! Special thanks to [Alex Chang](https://www.linkedin.com/in/alexychang/) for his brainstorming and insistence that I land the plane, [Bryan Mamaril](http://ficuscreative.com/) at Microsoft for his eagle eye, Jeremey Hanson for his editing notes, [Eric Braff](https://www.linkedin.com/in/eric-braff-276504b) for being the insane UI motion guru he is, Rob Girling at [Artefact](http://artefactgroup.com/) for his belief in me all those years ago, [Matt Silverman](http://www.swordfish-sf.com/) for his inspiring talk on UI motion at the After Effects conference, [Bradley Munkowitz](http://gmunk.com/) for being an awesome roommate and getting me inspired in UI, [Pasquale D’Silva](https://medium.com/@pasql) for his incredible articles on motion, [Rebecca Ussai Henderson](https://medium.freecodecamp.com/@becca_u) for her awesome article on UI Choreography, [Adrian Zumbrunnen](https://medium.com/@azumbrunnen) for his awesome contributions to the topic of UI and motion, [Wayne Greenfield](http://www.seattlekombucha.com/) and [Christian Brodin](http://www.theapartmentinvestor.com/author/christian-brodin/) for being my mastermind brothers and always pushing me to level up, and all you thousands of UI Animators who keep cranking out inspiring gifs. + + + +--- + +> [掘金翻译计划](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)、[React](https://github.com/xitu/gold-miner#react)、[前端](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)。 From 50a020d04b8ae6c32b5c5a02a0ef2b86cc16bdbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=B9=E5=8F=B7=E4=B8=89?= Date: Fri, 7 Apr 2017 22:00:19 +0800 Subject: [PATCH 061/638] Create swift-lazy-initialization-with-closures.md --- ...swift-lazy-initialization-with-closures.md | 362 ++++++++++++++++++ 1 file changed, 362 insertions(+) create mode 100644 TODO/swift-lazy-initialization-with-closures.md diff --git a/TODO/swift-lazy-initialization-with-closures.md b/TODO/swift-lazy-initialization-with-closures.md new file mode 100644 index 00000000000..00d175ee6b5 --- /dev/null +++ b/TODO/swift-lazy-initialization-with-closures.md @@ -0,0 +1,362 @@ +> * 原文地址:[Swift Lazy Initialization with Closures](https://blog.bobthedeveloper.io/swift-lazy-initialization-with-closures-a9ef6f6312c) +> * 原文作者:[Bob Lee](https://blog.bobthedeveloper.io/@bobthedev?source=post_header_lockup) +> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) +> * 译者: +> * 校对者: + + +# Swift Lazy Initialization with Closures # + +## Learn how to create objects with modularity and readability ## + +![](https://cdn-images-1.medium.com/max/2000/1*KNmIy5QAOeokXPW86TtVyA.png) + +Magic Keyboard 2 and Magic Mouse 2 + +*Welcome my lovely readers. Good to see you here today. For those who are new, I’m Bob. Just for real quick, if you wish to be on my mailing list, you can sign up* [*here*](https://boblee.typeform.com/to/oR9Nt2) *and get more value for your learning with iOS development :)* + +### Motivation ### + +In the beginning of my iOS journey, I followed tutorials on YouTube. I saw a few using something like below to create UI objects. + +``` +let makeBox: UIView = { + let view = UIView() + return view +}() +``` + +As a learner, I copied the practice and used it. One day, however, one of my readers asked me, “why do you add `{}` and why does `()` exist at the end? Is it a computed property?” I could not answer. I was a zombie. + +I wrote this tutorial for my younger self. Yet, some may find it useful. + +### Objectives ### + +There are three objectives. First, understand how to initialize an object using the unconventional way as shown above. Second, learn when to use `lazy var` in Swift. Last, join my mailing list. + +#### Prerequisites #### + +To fully enjoy the ride with me, I highly recommend you to be familiar with the topics below. + +1. [*Closures*](https://blog.bobthedeveloper.io/no-fear-closure-in-swift-3-with-bob-72a10577c564) +2. [*Capture List and retention cycle [weak self]*](https://blog.bobthedeveloper.io/swift-retention-cycle-in-closures-and-delegate-836c469ef128) +3. *Descent Object Oriented Programming* + +### Create UI Components ### + +Before I explain the unconventional method above, let’s look into your past. In order to create a button in Swift, you probably have done something like this, + +``` +// Determine Size +let buttonSize = CGRect(x: 0, y: 0, width: 100, height: 100) + +// Create Instance +let bobButton = UIButton(frame: buttonSize) +bobButton.backgroundColor = .black +bobButton.titleLabel?.text = "Bob" +bobButton.titleLabel?.textColor = .white +``` + +This is *Okay.* + +Assume, you have to create three other buttons, you probably have to copy the code above and then change the name from `bobButton` to `bobbyButton`. + +It’s quite tedious. + +``` +// New Button +let bobbyButton = UIButton(frame: buttonSize) +bobbyButton.backgroundColor = .black +bobbyButton.titleLabel?.text = "Bob" +bobbyButton.titleLabel?.textColor = .white +``` + +To make things just a bit easier, you may + +![](https://cdn-images-1.medium.com/max/800/1*oDIPy0i4YzUnKVR4XYI4kg.gif) + +This works too with the keyboard shortcut: ctrl-cmd-e + +If you don’t wish to repeat yourself, you may create a function instead. + +``` +func createButton(enterTitle: String) -> UIButton { + let button = UIButton(frame: buttonSize) + button.backgroundColor = .black + button.titleLabel?.text = enterTitle + return button +} + +createButton(enterTitle: "Yoyo") // 👍 +``` + +However, in iOS development, it is rare that custom buttons look similar. Therefore, a function may require a lot more parameters including background color, title, border radius, shadow, and so on. You function may end up looking like, + +``` +func createButton(title: String, borderWidth: Double, backgrounColor, ...) -> Button +``` + +The code above is not ideal even if you add default parameters to the function. It decreases readability. Therefore, it’s better to stay with the tedious method above. + +But, is there any way we can make it less tedious and more organized? Of course. We’ve looked into your past — It’s time to step up and look into your future. + +### Introducing the Unconventional Way ### + +Before we create UI components with the unconventional way, let’s first answer the initial questions my reader asked. What does `{}` mean, and is it a `computed property`? + +*Nope, it’s just a* ***closure block***. + +First, let’s demonstrate how to create an object using a closure. We will design a struct called `Human`. + +``` +struct Human { + init() { + print("Born 1996") + } +} +``` + +Now, this is how you create an object with a closure + +``` +let createBob = { () -> Human in + let human = Human() + return human +} + +let babyBob = createBob() // "Born 1996" +``` + +*If the syntax above doesn’t look familiar to you, you may stop reading now, and go to* [*Fear No Closure with Bob*](https://blog.bobthedeveloper.io/no-fear-closure-in-swift-3-with-bob-72a10577c564) *, and bring some bullets.* + +Just to explain, `createBob` is a closure whose type is `() -> Human`. You’ve created an instance called, `babyBob` by calling `createBob()` . + +However, you had to create two constants: `createBob` and `babyBob`. What if you want to do everything in a single statement? Here you go. + +``` +let bobby = { () -> Human in + let human = Human() + return human +}() +``` + +Now, the closure block executes itself through adding `()` at the end and `bobby` now has a `Human` object attached. Pretty good stuff. + +**You’ve learned how to initialize an object with a closure block.** + +Now, let’s apply to creating an UI object which should be similar to the example right above. + +``` +let bobView = { () -> UIView in + let view = UIView() + view.backgroundColor = .black + return view +}() +``` + +Great, we can make it shorter. In fact, we don’t need to specify the type of the closure block. Instead, all we have to do is specify the type of the instance, `bobView`, for example. + +``` +let bobbyView: **UIView** = { + let view = UIView() + view.backgroundColor = .black + return view +}() +``` + +Swift is able to infer that the closure block is `() -> UIView` based on the keyword, `return`. + +Now, take a look. The example right above should look identical to the “unconventional way” I feared. + +### Benefits of Init with Closures ### + +We discussed the tediousness of creating objects and the problem that arises from using a function. In your head, you must be thinking, “why should I use a closure block instead?” + +#### Easy to Duplicate #### + +I don’t like to use Storyboard, I love copy and pasting UI objects. In fact, I’ve a “library” of code in my computer. Let us assume that there is a button as shown below in the library. + +``` +let myButton: UIButton = { + let button = UIButton(frame: buttonSize) + button.backgroundColor = .black + button.titleLabel?.text = "Button" + button.titleLabel?.textColor = .white + button.layer.cornerRadius = + button.layer.masksToBounds = true +return button +}() +``` + +All I have to do is copy the entire lines, and then just change the name of `myButton` to `newButton` for the usage. Had I not used the closure method, I probably had to change the name of `button` to `newButton` 7–8 times. We could use the Xcode shortcut above, but why not make it just simpler. + +#### Look Cleaner #### + +Since objects are grouped together, it feels cleaner based on my eyes. Let’s compare + +``` +// Init with Closure +let leftCornerButton: UIButton = { + let button = UIButton(frame: buttonSize) + button.backgroundColor = .black + button.titleLabel?.text = "Button" + button.titleLabel?.textColor = .white + button.layer.cornerRadius = + button.layer.masksToBounds = true +return button +}() + +let rightCornerButton: UIButton = { + let button = UIButton(frame: buttonSize) + button.backgroundColor = .black + button.titleLabel?.text = "Button" + button.titleLabel?.textColor = .white + button.layer.cornerRadius = + button.layer.masksToBounds = true +return button +}() +``` + +vs + +``` +// Init With Fingers +let leftCornerButton = UIButton(frame: buttonSize) +leftCornerButton.backgroundColor = .black +leftCornerButton.titleLabel?.text = "Button" +leftCornerButton.titleLabel?.textColor = .white +leftCornerButton.layer.cornerRadius = +leftCornerButton.layer.masksToBounds = true + +let rightCornerButton = UIButton(frame: buttonSize) +rightCornerButton.backgroundColor = .black +rightCornerButton.titleLabel?.text = "Button" +rightCornerButton.titleLabel?.textColor = .white +rightCornerButton.layer.cornerRadius = +rightCornerButton.layer.masksToBounds = true +``` + +Although creating an object with the closure add a couple lines more, I feel less overwhelmed since I only have to add attributes to `button` rather than `rightCornerButton` or `leftCornerButton`. + +*In fact, if the name of a button gets more descriptive, often times it requires fewer lines to create an object with a closure block.* + +**You’ve accomplished the first objective. Congratulations** + +### Lazy Init Application ### + +You’ve come a long way. It’s time to meet the second objective of this tutorial. + +You might have seen something like this below + +``` +class IntenseMathProblem { + lazyvar complexNumber: Int = { + // imagine it requires a lot of CPU + 1 * 1 + }() +} +``` + +What `lazy` allows you to do is, the `complexNumber` property will be only calculated when you try to access it. For example, + +``` +let problem = IntenseMathProblem +problem() // No value for complexNumber +``` + +Currently, there is no value for `complexNumber`. However, once you access the property, + +``` +problem().complexNumber // Now returns 1 +``` + +The `lazy var` is often used to sort database and fetch data from any backend services because you definitely don’t want to calculate and sort everything when you create an object. + +*In fact, your phone will crash since the object is super bloated and the RAM can’t handle.* + +### Application ### + +Below is just an application of `lazy var`. + +#### Sorting #### + +``` +class SortManager { + lazy var sortNumberFromDatabase: [Int] = { + // Sorting logic + return [1, 2, 3, 4] + }() +} +``` + +#### Image Compression #### + +``` +class CompressionManager { + lazy var compressedImage: UIImage = { + let image = UIImage() + // Compress the image + // Logic + return image + }() +} +``` + +### Rules with `Lazy` ### + +1. You can’t use `lazy` with `let` since there is no initial value, and it is attained later when it is accessed. +2. You can’t use it with a `computed property` since computed property is always recalculated (requires CPU) when you modify any of the variables that has a relationship with the `lazy` property. +3. `Lazy` is only valid for members of a struct or class + +### Does Lazy Capture? ### + +So, if you’ve read the previous article on [Retention Cycle in Closures and Delegate](https://blog.bobthedeveloper.io/swift-retention-cycle-in-closures-and-delegate-836c469ef128) , you might wonder. Let’s test it out. Create a class called `BobGreet`. It has two properties: `name` whose type is `String` and `greeting` whose type is also `String` but initialized with a closure block. + +``` +class BobGreet { + var name = "Bob the Developer" + lazy var greeting: String = { + return "Hello, \(self.name)" + }() + +deinit { + print("I'm gone, bruh 🙆‍")} + } +} +``` + +The closure block *might* have a strong reference to `BobGuest` but let’s attempt to deallocate. + +``` +var bobGreet: BobGreet? = BobClass() +bobGreet?.greeting +bobClass = nil // I'm gone, bruh 🙆‍ +``` + +No need to worry about `[unowned self]` The closure block does not have a reference to the object. Instead, it just copies `self` within the closure block. If you are confused by the previous statement, feel free to read [Swift Capture Lists](https://blog.bobthedeveloper.io/swift-capture-list-in-closures-e28282c71b95) to learn more. 👍 + +### Last Remarks ### + +I learned a quite a bit while preparing for this tutorial. I hope you did as well. I’d appreciate your genuine fat ❤️. But, there is just one more. As the last objective, if you wish to on my mailing list and receive greater value from me, you can sign up right [**here**](https://boblee.typeform.com/to/oR9Nt2) . + +As you can see by the cover photo, I recently bought Magic Keyboard and Mouse. They are pretty good and increase my productivity a lot. You can get the mouse [here](http://amzn.to/2noHxgl) or the keyboard [here](http://amzn.to/2noHxgl). I never regret despite the price. 😓 + +> [Source Code](https://github.com/bobthedev/Blog_Lazy_Init_with_Closures) + +### Swift Conference I Will Join ### + +I will be joining my first conference @[SwiftAveir](https://twitter.com/SwiftAveiro) o from June 1–2. A friend of mine, [Joao](https://twitter.com/NSMyself) , is helping organize the conference, so I’m super excited. You can learn more about the event [here](http://swiftaveiro.xyz) ! + +#### Recommended Articles #### + +> Intro to Functional Programming ([Blog](https://blog.bobthedeveloper.io/intro-to-swift-functional-programming-with-bob-9c503ca14f13)) + +> My Favorite Xcode Shortcuts ([Blog](https://blog.bobthedeveloper.io/intro-to-swift-functional-programming-with-bob-9c503ca14f13) ) + +### Bob the Developer ### + +I’m an iOS instructor from Seoul, 🇰🇷. Feel free to get to know me on [Instagram](https://instagram.com/bobthedev) . I post regular updates on [Facebook Page](https://facebook.com/bobthedeveloper) and 🖨 on Sat 8pm EST. + +--- + +> [掘金翻译计划](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)、[React](https://github.com/xitu/gold-miner#react)、[前端](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)。 From 360e4e55dce24e687e9b1d80344131a2b70d5f58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=B9=E5=8F=B7=E4=B8=89?= Date: Fri, 7 Apr 2017 22:03:19 +0800 Subject: [PATCH 062/638] Update secure-web-app-http-headers.md --- TODO/secure-web-app-http-headers.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/TODO/secure-web-app-http-headers.md b/TODO/secure-web-app-http-headers.md index 4514e6c4c2a..e2f1066fd33 100644 --- a/TODO/secure-web-app-http-headers.md +++ b/TODO/secure-web-app-http-headers.md @@ -6,12 +6,6 @@ ## [How To Secure Your Web App With HTTP Headers](https://www.smashingmagazine.com/2017/04/secure-web-app-http-headers/) ## - -Today, too many websites are still inaccessible. In our new book *Inclusive Design Patterns*, we explore how to craft **flexible front-end design patterns** and make **future-proof and accessible interfaces without extra effort**. Hardcover, 312 pages. [Get the book now!](https://shop.smashingmagazine.com/products/inclusive-design-patterns?utm_source=magazine&utm_campaign=inclusive-design-patterns&utm_medium=html-ad-content-1) - -[![](https://www.smashingmagazine.com/wp-content/uploads/2016/10/inclusive-design-pattners-250px.png)](https://shop.smashingmagazine.com/products/inclusive-design-patterns?utm_source=magazine&utm_campaign=inclusive-design-patterns&utm_medium=html-ad-content-1) - - Web applications, be they thin websites or thick single-page apps, are notorious targets for cyber-attacks. In 2016, approximately [40% of data breaches](http://www.verizonenterprise.com/verizon-insights-lab/dbir/2016/) originated from attacks on web apps — the leading attack pattern. Indeed, these days, understanding cyber-security is not a luxury but rather **a necessity for web developers**, especially for developers who build consumer-facing applications. HTTP response headers can be leveraged to tighten up the security of web apps, typically just by adding a few lines of code. In this article, we’ll show how web developers can use HTTP headers to build secure apps. While the code examples are for Node.js, setting HTTP response headers is supported across all major server-side-rendering platforms and is typically simple to set up. From 3a3a9df00e43ef7a88fb8d62a78c464ce9d0d44a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=B9=E5=8F=B7=E4=B8=89?= Date: Fri, 7 Apr 2017 22:15:05 +0800 Subject: [PATCH 063/638] Create on-loser-experience-design.md --- TODO/on-loser-experience-design.md | 70 ++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 TODO/on-loser-experience-design.md diff --git a/TODO/on-loser-experience-design.md b/TODO/on-loser-experience-design.md new file mode 100644 index 00000000000..733b4435aba --- /dev/null +++ b/TODO/on-loser-experience-design.md @@ -0,0 +1,70 @@ +> * 原文地址:[On Loser Experience Design](https://medium.com/on-human-centric-systems/on-loser-experience-design-1916629c36fc) +> * 原文作者:[Matt LeMay](https://medium.com/@mattlemay?source=post_header_lockup) +> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) +> * 译者: +> * 校对者: + +# On Loser Experience Design # + +## Designing platforms and products that aren’t just for winners, “influencers,” and “thought leaders.” ## + +![](https://cdn-images-1.medium.com/max/800/1*kVOEiUv3YK8tcYEa5QKmLA.jpeg) + +Turntable.fm, a Great Lesson in Loser Experience Design + +Do you remember turntable.fm? The idea behind the product was simple and brilliant: you join a virtual “room” with friends and/or strangers, and take turns DJing for each other. Like a lot of products that experience meteoric early-stage growth, it took a real-world behavior — social listening — and created a digital proxy that could instantly connect people across geographic and cultural divides. I was super-excited when turntable.fm launched, and I was not alone. + +But the more I used turntable.fm, the more I started to feel like — well, a *loser*. I was primarily interested in discovering new music, not in becoming a **star virtual DJ**. But the platform seemed to offer no encouragement or incentives for being a good listener, no shiny prizes for thoughtfully engaging in conversation with my virtual roommates. My avatar remained puny and plain, while others grew mighty, adorned with jewels, and outfitted with cool animal costumes. Eventually, I just gave up. + +**Turntable.fm suffered from bad loser experience design: making casual users feel like unwitting competitors in a game they cannot win.** + +In the absence of good loser experience design, products and platforms turn into ghost towns inhabited by thirsty would-be “influencers,” howling desperately into a void that was once occupied by curious, casual users. And in a world obsessed with performance metrics and status markers, bad loser experience design is all around us. + +#### The Patterns of Bad Loser Experience Design #### + +Here are a few signs that your product may be suffering from **bad loser experience design:** + +![](https://cdn-images-1.medium.com/max/600/1*k_ZpnygG7JhLUteHxZOJyQ.png) + +Illustration by [Joan LeMay](http://joanlemay.com) + +- **You have a “leaderboard” or a points system.** + +Yes, in the short-term, people may engage with a product for an abstract reward such as “points” or “[coins](https://techcrunch.com/2015/12/09/swarm-now-lets-you-spend-those-coins-on-upgraded-stickers/) .” But watch what happens as your users see themselves fall to the bottom of that “leaderboard” or fail to get any real value out of the time they’ve invested in earning those shiny trinkets. Competing for something only to realize that it’s worthless is embarrassing, frustrating, and makes you feel *like a huge loser*. Gratuitous “gamification” is one of the most odious and lazy patterns of bad loser experience design — and in the long term, it [doesn’t work](http://www.gartner.com/newsroom/id/2251015) . + +- **You visually distinguish the winners from the losers.** + +Twitter’s verification system was designed to foster trust by “verifying” that people are who they say they are. But in its early days, “verification” felt like a [badge of honor for “important” people](http://anildash.com/2013/03/what-its-like-being-verified-on-twitter.html) and an [aspirational delineator between the haves and have-nots](http://www.xojane.com/tech/how-to-get-verified-on-twitter) . Twitter took a great step towards mitigating this bad loser experience design when they bulk-verified journalists, making it clear that there was a functional *purpose* to this distinction and conferring it on people who are not high-status celebrities. + +- **You over-index on popularity metrics.** + +This is perhaps the most widespread pattern of bad loser experience design. Trying to decide what content to surface? How about the most popular content! Trying to find some “interesting” users? Look for the ones who have the most followers! This is the “rich get richer” problem of bad loser experience design — by building platforms that reward the people who are already succeeding, you create a permanent and inaccessible overclass that tends to reward the people who are the most ruthless and aggressive about increasing their own status — even if it means gaming the system. + +- **You treat casual users like failed power users.** + +Zach Holman’s “[Don’t Give Your Users the Shit Work](https://zachholman.com/posts/shit-work/) ” does a great job of describing this bad loser experience pattern in action: a new user gets onboarded, and is immediately presented with a barrage of “power user” features that will help them get the *most* out of this product. If they neglect to use these features, they find themselves stuck in a kind of permanent limbo, nagged by reminders to use features they don’t need and unable to derive immediate value from the product. If you treat casual users like failed power users, they will start to *feel *like failures — and they will leave. + +#### Tips for Good Loser Experience Design #### + +While bad loser experience design can significantly harm a product, *good* loser experience design can help foster a broad, engaged, and self-sustaining user base. Here are a few tips for good loser experience design: + +- **Help people find *their* people, not the most popular people.** + +When platforms focus on shared interests and social bonds over “likes” and “favorites,” they help everybody find a place where they belong. Instagram has done a great job doing this with their discovery features, consistently surfacing people who are adjacent to *your* people, not people with the most likes or followers. Their new discovery features have helped me find my [guitar](https://www.instagram.com/leoleoband/) [people](https://www.instagram.com/lostincrystalcanyons/) , my [cat](https://www.instagram.com/scruffles_fatcat/)[people](https://www.instagram.com/12catslady/) , and even to discover a whole new community of [prairie](https://www.instagram.com/rinran032/) [dog](https://www.instagram.com/prairiedogpack/) [people](https://www.instagram.com/pimpa_wan/) . As I’ve used these discovery features more, I’ve started to pay *less attention to how many “follows” and “likes” I get, and more attention to the people with whom I interact* — which is a surefire signal of great loser experience design. + +- **Give “passive” users meaningful ways to engage.** + +Tumblr’s reblog feature is a great example of good loser experience design. If somebody sees something they like, they can *share it to their entire network*, rather than just leaving a comment or a “like.” This helps people engage with each other more deeply, and means that a post written by somebody with a small number of followers can find a wide audience as it makes its way through networks and communities of people. It also gives people who don’t want to “write” a post a meaningful way to contribute and engage. + +- **Test your product with people who are not “power users.”** + +Finally, and perhaps most importantly, break out of the design and testing patterns that lead to equating “power users” with “good users.” Over-reliance on internal “dogfooding,” where new products and features are tested primarily with a company’s own employees, is a one-way ticket to bad loser experience design. Dismissing user testing candidates who are not over the moon for your product is another surefire road to bad loser experience design. Think through the needs and behaviors of casual users as extensively as you think through those of “power users” — and ask yourself, “if I only use this product a few times a week, will it make me feel like a *loser*?” + +#### Loser Experience Design in the Wild #### + +Has your day-to-day usage of a product or platform been affected by bad loser experience design? Leave a response and let me know your thoughts. And be sure to hit that “like” button, so that I can feel like a high-status *winner* right here on medium dot com. + + +--- + +> [掘金翻译计划](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)、[React](https://github.com/xitu/gold-miner#react)、[前端](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)。 From c7230b4ac0ba734d2c4062851c97149fcf7a2270 Mon Sep 17 00:00:00 2001 From: sqrtthree Date: Fri, 7 Apr 2017 22:22:00 +0800 Subject: [PATCH 064/638] update link --- TODO/swift-lazy-initialization-with-closures.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO/swift-lazy-initialization-with-closures.md b/TODO/swift-lazy-initialization-with-closures.md index 00d175ee6b5..051ab41f287 100644 --- a/TODO/swift-lazy-initialization-with-closures.md +++ b/TODO/swift-lazy-initialization-with-closures.md @@ -1,5 +1,5 @@ > * 原文地址:[Swift Lazy Initialization with Closures](https://blog.bobthedeveloper.io/swift-lazy-initialization-with-closures-a9ef6f6312c) -> * 原文作者:[Bob Lee](https://blog.bobthedeveloper.io/@bobthedev?source=post_header_lockup) +> * 原文作者:[Bob Lee](https://blog.bobthedeveloper.io/@bobthedev) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 译者: > * 校对者: From de20c171c428fc387c46314efd0266f8710ce9bb Mon Sep 17 00:00:00 2001 From: zhuzi Date: Sat, 8 Apr 2017 00:22:18 +0800 Subject: [PATCH 065/638] update according to proof-reading --- TODO/beyond-browser-web-desktop-apps.md | 140 ++++++++++++------------ 1 file changed, 69 insertions(+), 71 deletions(-) diff --git a/TODO/beyond-browser-web-desktop-apps.md b/TODO/beyond-browser-web-desktop-apps.md index 7b4f8ffcec8..9803dd3b2c6 100644 --- a/TODO/beyond-browser-web-desktop-apps.md +++ b/TODO/beyond-browser-web-desktop-apps.md @@ -2,31 +2,31 @@ > * 原文作者:本文已获原作者 [Adam Lynch](https://www.smashingmagazine.com/author/adamlynch/) 授权 > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 译者: [bambooom](https://github.com/bambooom)/[imink](https://github.com/imink) -> * 校对者: +> * 校对者:[bambooom](https://github.com/bambooom)/[sunui](https://github.com/sunui) ## 超越浏览器:从 web 应用到桌面应用 -一开始我是个 web 开发者,现在我是个全栈开发者,但从未想过在桌面上有所作为。我热爱 web 技术,热爱这个无私的社区,热爱它对于开源的友好,尝试挑战极限。我热爱探索好看的网站和强大的应用。当我被指派做桌面应用的任务的时候,我非常忧虑和害怕,因为那看起来很难,或者至少非常不同。 +一开始我是个 web 开发者,现在我是个全栈开发者,但从未想过在桌面上有所作为。我热爱 web 技术,热爱这个无私的社区,热爱它对于开源的友好,尝试挑战极限。我热爱探索好看的网站和强大的应用。当我被指派做桌面应用任务的时候,我非常忧虑和害怕,因为那看起来很难,或者至少不一样。 这并不吸引人,对吧?你需要学一门新的语言,甚至三门?想象一下过时的工作流,古旧的工具,没有任何你喜欢的有关 web 的一切。你的职业发展会被怎样影响呢? -别慌,深呼吸,现实情况是,作为 web 开发者,你已经拥有一切开发现代桌面应用所需的一切技能,感谢新的强大的 API,你甚至可以在桌面应用中发挥你最大的潜能。 +别慌,深呼吸,现实情况是,作为 web 开发者,你已经拥有开发现代桌面应用所需的一切技能,得益于新的强大的 API,你甚至可以在桌面应用中发挥你最大的潜能。 本文将会介绍使用 [NW.js](http://nwjs.io/) 和 [Electron](https://electron.atom.io/) 开发桌面应用,它们的优劣,使用同一套代码库给桌面、web,甚至更多。 ### 为什么? -首先,为什么会有人开发桌面应用?任何现有的 web 应用(不同于网站,如果你认为它们是不同的)都可能适合变成一个桌面应用。你可以围绕任何可以从与用户系统集中中获益的 web 应用构建桌面应用;例如本地通知,开机启动,与文件的交互等。有些用户单纯更喜欢在自己的电脑中永久保存一些 app,无论是否联网都可以访问。 +首先,为什么会有人开发桌面应用?任何现有的 web 应用(不同于网站,如果你认为它们是不同的)都可能适合变成一个桌面应用。你可以围绕任何可以从与用户系统集成中获益的 web 应用构建桌面应用;例如本地通知,开机启动,与文件的交互等。有些用户单纯更喜欢在自己的电脑中永久保存一些 app,无论是否联网都可以访问。 也许你有个想法,但只能用作桌面应用,有些事情只是在 web 应用中不可能实现(至少还有一点,但更多的是这一点)。你可能想要为公司内部创建一个独立的功能性应用程序,而不需要任何人安装除了你的 app 之外的任何内容(因为内置 Node.js )。也许你有个有关 Mac 应用商店的想法,也许只是你的一个个人兴趣的小项目。 -很难总结为什么你应该考虑开发桌面应用,因为真的有很多类型的应用你可以创建。这非常取决于你想要达到什么目的,API 是否足够有利于开发,离线使用将多大程度上增强用户体验。在我的团队,这些都是毋庸置疑的,因为我们在开发一个[聊天应用程序](https://teamwork.com/chat)。另一方面来说,一个依赖于网络而没有任何与系统集成的桌面应用应该是一个 web 应用,并且只是 web 应用。当用户并不能从桌面应用中获得比在浏览器中访问一个网址更多的价值的时候,期待用户下载你的应用(其中自带浏览器以及 Node.js)是不公平的。 +很难总结为什么你应该考虑开发桌面应用,因为真的有很多类型的应用你可以创建。这非常取决于你想要达到什么目的,API 是否足够有利于开发,离线使用将多大程度上增强用户体验。在我的团队,这些都是毋庸置疑的,因为我们在开发一个[聊天应用程序](https://teamwork.com/chat)。另一方面来说,一个依赖于网络而没有任何与系统集成的桌面应用应该做成一个 web 应用,并且只做 web 应用。当用户并不能从桌面应用中获得比在浏览器中访问一个网址更多的价值的时候,期待用户下载你的应用(其中自带浏览器以及 Node.js)是不公平的。 -比起描述你个人应该建造的桌面应用以及为什么建造,我更希望的是激发一个想法,或者只是激发你对这篇文章的兴趣。继续往下读来看看用 web 技术构造一个强大的桌面应用是多么简单,以及需要承受的困难超过(或者相同)创造一个 web 应用。 +比起描述你个人应该建造的桌面应用及其原因,我更希望的是激发一个想法,或者只是激发你对这篇文章的兴趣。继续往下读来看看用 web 技术构造一个强大的桌面应用是多么简单,以及创建完成(或者在这过程中)一个 web 应用你需要承受什么。。 ### NW.js -桌面应用已经有很长一段时间了,我知道你没有很多时间,所以我们跳过一些历史,从2011年的上海开始。来自 Intel 开源技术中心的 Roger Wang 创造了 node-webkit,一个概念验证的 Node.js 模块,这个模块可以让用户生成一个自带 WebKit 内核的浏览器窗口并直接在 ` + + + + + + +
+ + You signed in with another tab or window. Reload to refresh your session. + You signed out in another tab or window. Reload to refresh your session. +
+ + + + + -> [掘金翻译计划](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)、[React](https://github.com/xitu/gold-miner#react)、[前端](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)。 From dea537b873dcacec4bc5b2ea99a122b7767799dc Mon Sep 17 00:00:00 2001 From: Wenlin Ou Date: Fri, 7 Apr 2017 22:02:31 -0400 Subject: [PATCH 071/638] ... --- ...ng-will-change-until-you-start-building.md | 807 +----------------- 1 file changed, 47 insertions(+), 760 deletions(-) diff --git a/TODO/nothing-will-change-until-you-start-building.md b/TODO/nothing-will-change-until-you-start-building.md index 46f78f0efb5..0d57984107d 100644 --- a/TODO/nothing-will-change-until-you-start-building.md +++ b/TODO/nothing-will-change-until-you-start-building.md @@ -1,803 +1,90 @@ +> * 原文地址:[Nothing will change until you start building.](https://medium.freecodecamp.com/nothing-will-change-until-you-start-building-2681e85e7bdc) +> * 原文作者:[Jonathan Z. White](https://medium.freecodecamp.com/@JonathanZWhite?source=post_header_lockup) +> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) +> * 译者: +> * 校对者: +--- +![](https://cdn-images-1.medium.com/max/1000/1*EwHpnCZ70FtJMi-lNSl-9Q.png) +# Nothing will change until you start building. +This past week, I was in a Lyft. My driver was telling me about all of her ideas for side projects. She had ideas for a children’s book, an app that helps people find parking, and a more efficient way to package gifts. The problem was she was frozen by indecision. She had the ideas but she didn’t know where to start. +This is what I told her. **Start by building. Pick one project and do whatever you have to do to ship it.** If you want to write a book, start with writing a page a day. If you want to build an app, start with some sketches. Anyone can do it. - - - - +This advice applies to all creators. Once you start building and launching your projects, you won’t be able to stop. Building will become part of your identity. And even if your project fails, you’ll keep at it. +The steps you take today will compound over time. Look at great product makers like [Drew Wilson](https://twitter.com/drewwilson), [Pieter Levels](https://twitter.com/levelsio?), and [Sebastian Dobrincu](https://twitter.com/Sebyddd). **They don’t wait for the right opportunity.** They build and ship weekly. +In an interview on [IndieHackers](https://www.indiehackers.com/podcast/006-josh-pigford-of-baremetrics), [Josh Pigford](https://twitter.com/Shpigford) disclosed that before he built Baremetrics, he had iterated through a *hundred* failed ideas. Today, Baremetrics makes over 60,000 in monthly recurring revenue. - - - - - - - - - gold-miner/nothing-will-change-until-you-start-building.md at translate · owenlyn/gold-miner - - - +![](https://cdn-images-1.medium.com/max/800/1*BzmVaqAKEzNRybUmMYOxdA.png) - - +With that in mind, here are some tactics I’ve compiled from friends and acquaintances who are successful product makers. - - - - - - +--- - +### 1. Define your goal - - - +Early on, figure out the main goal of your project. If you want to break out of your usual design style, focus on the design. If you want to test a new framework for your front-end, focus on the code. If you want to acquire a hundred users, focus on selling. - - +**Define your goal early so you don’t get distracted and end up not achieving what you set out to do.** Perhaps your project doesn’t become the next Snapchat, but by working towards your goal you will have honed the tools in your product-making arsenal. Little by little, you’ll be able to design, code, and sell faster and more efficiently. +### 2. Stay on track +Sometimes product makers fail to ship because they lose motivation. **Set small goals; meeting those goals will help you build momentum.** Small wins will compound. +![](https://cdn-images-1.medium.com/max/800/1*ESildSVTSSOnFXGDxD-l9w.png) - +Another thing you can do to stay on track is to be more public about your work. Share your progress online and with your peers. People love to see things like wireframes and will often times provide valuable input. +One trick that I find particularly motivating is making bets with friends. I tell them, if I don’t complete a task by a deadline, I’ll give them a hundred dollars. For example, I might set my task to getting twenty people to pay me within two weeks of launching a project. - +### 3. Solve a problem - - +Start by solving a problem. If you have experienced the problem yourself, even better. Ask your friends what kind of problems they experience. - - +For example, I built [YC Careers](http://jonathanzwhite.github.io/yc-careers/) and [AtomSpace](https://atomspace.co/) to address the needs of one my friends who was having trouble applying to jobs and interviewing as a product designer. Overnight, YC Careers made it to the top of [ProductHunt](https://www.producthunt.com/posts/yc-careers) and AtomSpace booked $100 dollars from strangers within 6 hours of going live. +**The bigger the pain point you’re trying to solve, the easier it will be to find users.** - +Also, analyze problem-solutions from multiple perspectives. Some successful ideas might seem like a solution without a problem. For example, Instagram doesn’t seem to solve any immediate problem. But it does. Instagram solves the need for friends to stay updated on one another. If this need is not met, then it becomes a problem. - - +For further reading, [John Carmack on Idea Generation](https://amasad.me/carmack) and [How to Get Startup Ideas](http://paulgraham.com/startupideas.html) are two great essays to get you started. - - - +### 4. Ditch the “good idea / bad idea” mindset - - +Once you’ve identified a problem and come up with a solution, you’ll probably ask yourself if your idea is good. +**Plenty of seemingly bad ideas have turned out to be great businesses.** For example, no one wanted to invest in Airbnb. Brian Chesky, one of the co-founders of Airbnb, detailed their rejections in his essay [7 Rejections](https://medium.com/@bchesky/7-rejections-7d894cbaa084#.l8fdqlasz). - +![](https://cdn-images-1.medium.com/max/800/1*WpxUxMCO-7NXr-o1yo023g.png) +The only way to know for sure if your idea will work is to formulate a product hypothesis and run an experiment to test it. Talk to people and ask if they are interested in your solution. If you can get them to pay you before you’ve built anything, even better. - +### 5. Ask for help - +When working on a project, reach out to people and ask for their help. **You will be surprised at how many people are willing to pay it forward and help a complete stranger.** - - +Some of the best advice and feedback I have received came from reaching out to people with an email or a DM on Twitter. - +Also, when asking for help, be specific and prepare your questions. If you want feedback on a design, send the mockup. If you want feedback on your marketing strategy, detail what you have tried so far. Context is key. And if you don’t get a response, follow up politely. Sometimes the person you’re reaching out to might have genuinely missed your message. +--- - +Build and launch your side projects. Use this process as a way to hone your craft so that when opportunity presents itself, you’re ready to take full advantage of it. Luck always favors the prepared. - +So next time you come up with an idea, act on it. Identify a problem, define your goal, stay on track, and ask for help. - - +What are you working on right now? How can I help you? Leave me a note here or [tweet](https://twitter.com/jonathanzwhite) them to me on Twitter. +You can find me on Medium where I publish every week. Or you can follow me on [Twitter](https://twitter.com/JonathanZWhite), where I post non-sensical ramblings about design, front-end development, and virtual reality. -
- Skip to content -
+*P.S. If you enjoyed this article, it would mean a lot if you click the 💚 and share with friends.* - - - - - - - - - - - - -
- -
- -
-
- - - -
-
-
- - - -
-
- - -
    -
  • -
    - -
    - - - - Unwatch - - - - -
    -
    -
    - - Notifications -
    - - - -
    -
    -
    -
    -
  • - -
  • -
    -
    - - -
    -
    - - -
    - -
  • - -
  • - - - Fork - - - - - -
  • -
- -

- - /gold-miner - - - forked from xitu/gold-miner - -

- -
- -
- -
-
- - - - -Permalink - - - -
- -
- - -
- -
-
- - Switch branches/tags -
- -
-
- -
-
- -
-
- - - -
-
- - -
- -
Nothing to show
-
- -
-
-
- -
- - Find file - - -
- -
- - - -
- - - d79d43a - - Apr 7, 2017 - - - -
- - @owenlyn - @sqrthree - - -
- - -
- -
-
-
- -
- Raw - Blame - History -
- - - - - -
- -
- -
- -
- 91 lines (47 sloc) - - 7.57 KB -
-
- - -
-
- -
-
-

-

真正行动之前 你将一无所成

-

就在上周我打了一辆 Lyft 出租车,司机跟我聊了很多关于她的天花乱坠的想法。她的想法里,有一个关于儿童的书籍,有帮助人们停车的APP、有一个更高效的打包礼物的方法,但问题是她总是犹豫不决:她又想法但却不知道从何开始。

-

接下来这段话是我对她说的:开始一些实际行动吧。选一个项目然后尽可能的去完成它。 如果你想写一本书,那么从每天写一页开始;如果你像做一个APP,那就从草稿开始。任何人都可以做到这些。

-

这些建议对所有创新者都适用。一旦你开始着手在你的项目上,你将会不由自主的继续做下去。创新将会成为你人格的一部分 —— 即使你的项目失败了。

-

你今天迈出的一小步将会是你人生的一大步。看看那些伟大的产品创造者吧,比如 Drew Wilson, Pieter Levels, 和 Sebastian Dobrincu他们不仅仅是等待合适的时机。 他们每周都在更新他们的产品。

-

在一个 IndieHackers 的节目中, Josh Pigford 透露,在做 Baremetrics 之前,他已经尝试过 一百个 失败的点子。 现在, Baremetrics 每个月都会有 60,000 美金进账。

-

-

记住这些,接下来的要说的,是我总结的一些来自成功的产品创作者朋友们的策略。

-
-

1. 确定目标

-

在开始的时候,确定你的项目要实现的主要目标。如果你想打破你以往的设计风格,那就把心思花在设计上;如果你测试一个新的前端框架,那就把钻研代码;如果你想获得上百个用户(”大量用户“ 会不会好点),就把精力用在销售上。

-

尽早确定目标可以帮助你摆脱诱惑,防止你到最后忘记初心。** 也许你的项目并不能成为下一个 Snapchat, 但是在通往你目标的道路上,你会更加的熟练运用各种做产品的工具。渐渐地,你会拥有越来越强大的设计、代码、销售等的能力。

-

2. 执行计划

-

有一些产品没能完的原因是作者在半路失去了动力。 设定一些小目标(比如先赚一个亿);完成这些目标会让你获得前进的动力。 小的成就累计起来也是很了不起的。

-

-

另一个让你保持动力的建议就是让你的项目更加公开。在网上和你的同行们分享你的进展。人们很喜欢看到线框(wireframe 翻译成 ”框架“ 会不会好一点?)类的东西,并常常带来有价值的投入。

-

我自己找到的一个非常有用的保持动力的小秘诀是和朋友打赌。我对他们说,如果我在截止日期前没有完成某个任务,我就给他们一百刀。比如,我会在一个项目开始的两周内找到二十个人打赌。

-

3. 解决问题

-

解决一个问题。如果你自己也经历了这个问题那就再好不过了。问问你的朋友们他们都有哪些问题需要解决。

-

比如,我做了 YC CareersAtomSpace 来解决一个产品设计师朋友找工作和面试的烦恼。一夜之间,YC Careers 一下子爬到了 ProductHunt 的榜首,AtomSpace 在上线6个小时就拿到了来自陌生人的一百美元订单。

-

你解决越大的痛点,就越容易找到用户。

-

同时,尝试从过个角度分析问题和解决方案。有些成功的点子看起来并没有解决任何问题,但其实不然。比如,Instagram 看起来没有直接解决任何问题。其实不是。Instagram 满足了朋友之间实时社交的需求。如果这个需求不能得到满足,那才是个问题。

-

想要了解更多的话,John Carmack on Idea GenerationHow to Get Startup Ideas 这两篇文章是非常好的开始。

-

4. 放弃”好主意/坏主意“的想法

-

当你明确了你想要解决的问题并有了一个解决方案的时候,你可能会问自己这是不是一个好的点子。

-

相当一部分看起来很糟糕的点子最后被证明是超级棒的生意。 比如,多年没人想投资 Airbnb。 Brian Chesky, Airbnb 的创始人之一,在他的文章 7 Rejections 里详细讲述了被投资人拒绝的故事。

-

-

唯一验证你想法的办法就是做出一个产品来测试它。 找人聊聊,看看他们对你的解决方案是不是感兴趣。如果在你做出任何产品之前就有人原意为之付费那就再棒不过了。

-

5. 寻求帮助

-

** 当你在你的项目上埋头苦干的时候,记得寻求别人的帮助。 你会很惊喜的发现竟然有这么多人原意去帮助一个陌生人(对,就是你)。

-

我获得的最好的一部分建议就是来自于给别人发邮件或是推特(微博)私信。

-

在寻求帮助的时候,请提一些精确、有价值的问题。如果你需要关于设计的一些反馈,就把草稿发给别人;如果你需要营销方面的建议,就详细的列出你尝试过的方法。内容才是关键。如果别人没有回复你,请礼貌的再次询问 —— 有时候别人只是没注意到你的消息而已。

-
-

开始你的兴趣项目吧。利用这个过程磨练自己,这样当机会来临的时候,你才能紧紧抓住。记住,机会总是青睐有准备的人。

-

所以下次你有一个想法的时候,采取行动吧。找到一个问题,确定你的目标,坚持下去,别忘了学会寻求帮助。

-

你现在在做神马项目呢?有什么是我能帮到你的吗?可以在这里或者我的 tweet 下面留言。

-

我在 Medium 上每周都会发文。也可以关注我的 Twitter,我会发一些关于设计、前端开发和虚拟现实的杂想。

-

P.S. If you enjoyed this article, it would mean a lot if you click the 💚 and share with friends.(可删?)

-
-
-

掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOSReact前端后端产品设计 等领域,想要查看更多优质译文请持续关注 掘金翻译计划

-
-
-
- -
- - - - - -
- -
- -
-
- -
- - - - - - - -
- - - You can't perform that action at this time. -
- - - - - - - - - -
- - You signed in with another tab or window. Reload to refresh your session. - You signed out in another tab or window. Reload to refresh your session. -
- - - - - +--- +> [掘金翻译计划](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)、[React](https://github.com/xitu/gold-miner#react)、[前端](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)。 \ No newline at end of file From b3a1f53af854a7ca405d5e19b9343feb4195b227 Mon Sep 17 00:00:00 2001 From: Wenlin Ou Date: Fri, 7 Apr 2017 22:04:58 -0400 Subject: [PATCH 072/638] ready for proofreading --- ...ng-will-change-until-you-start-building.md | 70 +++++++++---------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/TODO/nothing-will-change-until-you-start-building.md b/TODO/nothing-will-change-until-you-start-building.md index 0d57984107d..26cc605b437 100644 --- a/TODO/nothing-will-change-until-you-start-building.md +++ b/TODO/nothing-will-change-until-you-start-building.md @@ -1,90 +1,90 @@ > * 原文地址:[Nothing will change until you start building.](https://medium.freecodecamp.com/nothing-will-change-until-you-start-building-2681e85e7bdc) > * 原文作者:[Jonathan Z. White](https://medium.freecodecamp.com/@JonathanZWhite?source=post_header_lockup) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 译者: +> * 译者:[owenlyn](https://github.com/owenlyn) > * 校对者: --- ![](https://cdn-images-1.medium.com/max/1000/1*EwHpnCZ70FtJMi-lNSl-9Q.png) -# Nothing will change until you start building. +# Nothing will change until you start building. 真正行动之前 你将一无所成 -This past week, I was in a Lyft. My driver was telling me about all of her ideas for side projects. She had ideas for a children’s book, an app that helps people find parking, and a more efficient way to package gifts. The problem was she was frozen by indecision. She had the ideas but she didn’t know where to start. +This past week, I was in a Lyft. My driver was telling me about all of her ideas for side projects. She had ideas for a children’s book, an app that helps people find parking, and a more efficient way to package gifts. The problem was she was frozen by indecision. She had the ideas but she didn’t know where to start.上周我打了一辆 Lyft 出租车,司机跟我聊了很多关于她的天花乱坠的想法。她的想法里,有一个关于儿童的书籍,有帮助人们停车的APP、有一个更高效的打包礼物的方法,但问题是她总是犹豫不决:她又想法但却不知道从何开始。 -This is what I told her. **Start by building. Pick one project and do whatever you have to do to ship it.** If you want to write a book, start with writing a page a day. If you want to build an app, start with some sketches. Anyone can do it. +This is what I told her. **Start by building. Pick one project and do whatever you have to do to ship it.** If you want to write a book, start with writing a page a day. If you want to build an app, start with some sketches. Anyone can do it. 接下来这段话是我对她说的:**开始一些实际行动吧。选一个项目然后尽可能的去完成它。** 如果你想写一本书,那么从每天写一页开始;如果你像做一个APP,那就从草稿开始。任何人都可以做到这些。 -This advice applies to all creators. Once you start building and launching your projects, you won’t be able to stop. Building will become part of your identity. And even if your project fails, you’ll keep at it. +This advice applies to all creators. Once you start building and launching your projects, you won’t be able to stop. Building will become part of your identity. And even if your project fails, you’ll keep at it. 这些建议对所有创新者都适用。一旦你开始着手在你的项目上,你将会不由自主的继续做下去。创新将会成为你人格的一部分 —— 即使你的项目失败了。 -The steps you take today will compound over time. Look at great product makers like [Drew Wilson](https://twitter.com/drewwilson), [Pieter Levels](https://twitter.com/levelsio?), and [Sebastian Dobrincu](https://twitter.com/Sebyddd). **They don’t wait for the right opportunity.** They build and ship weekly. +The steps you take today will compound over time. Look at great product makers like [Drew Wilson](https://twitter.com/drewwilson), [Pieter Levels](https://twitter.com/levelsio?), and [Sebastian Dobrincu](https://twitter.com/Sebyddd). **They don’t wait for the right opportunity.** They build and ship weekly. 你今天迈出的一小步将会是你人生的一大步。看看那些伟大的产品创造者吧,比如 [Drew Wilson](https://twitter.com/drewwilson), [Pieter Levels](https://twitter.com/levelsio?), 和 [Sebastian Dobrincu](https://twitter.com/Sebyddd)。**他们不仅仅是等待合适的时机。** 他们每周都在更新他们的产品。 -In an interview on [IndieHackers](https://www.indiehackers.com/podcast/006-josh-pigford-of-baremetrics), [Josh Pigford](https://twitter.com/Shpigford) disclosed that before he built Baremetrics, he had iterated through a *hundred* failed ideas. Today, Baremetrics makes over 60,000 in monthly recurring revenue. +In an interview on [IndieHackers](https://www.indiehackers.com/podcast/006-josh-pigford-of-baremetrics), [Josh Pigford](https://twitter.com/Shpigford) disclosed that before he built Baremetrics, he had iterated through a *hundred* failed ideas. Today, Baremetrics makes over 60,000 in monthly recurring revenue. 在一个 [IndieHackers](https://www.indiehackers.com/podcast/006-josh-pigford-of-baremetrics) 的节目中, [Josh Pigford](https://twitter.com/Shpigford) 透露,在做 Baremetrics 之前,他已经尝试过 *一百个* 失败的点子。 现在, Baremetrics 每个月都会有 60,000 美金进账。 ![](https://cdn-images-1.medium.com/max/800/1*BzmVaqAKEzNRybUmMYOxdA.png) -With that in mind, here are some tactics I’ve compiled from friends and acquaintances who are successful product makers. +With that in mind, here are some tactics I’ve compiled from friends and acquaintances who are successful product makers.记住这些,接下来的要说的,是我总结的一些来自成功的产品创作者朋友们的策略。 --- -### 1. Define your goal +### 1. Define your goal确定目标 -Early on, figure out the main goal of your project. If you want to break out of your usual design style, focus on the design. If you want to test a new framework for your front-end, focus on the code. If you want to acquire a hundred users, focus on selling. +Early on, figure out the main goal of your project. If you want to break out of your usual design style, focus on the design. If you want to test a new framework for your front-end, focus on the code. If you want to acquire a hundred users, focus on selling.在开始的时候,确定你的项目要实现的主要目标。如果你想打破你以往的设计风格,那就把心思花在设计上;如果你测试一个新的前端框架,那就把钻研代码;如果你想获得上百个用户(”大量用户“ 会不会好点),就把精力用在销售上。 -**Define your goal early so you don’t get distracted and end up not achieving what you set out to do.** Perhaps your project doesn’t become the next Snapchat, but by working towards your goal you will have honed the tools in your product-making arsenal. Little by little, you’ll be able to design, code, and sell faster and more efficiently. +**Define your goal early so you don’t get distracted and end up not achieving what you set out to do.** Perhaps your project doesn’t become the next Snapchat, but by working towards your goal you will have honed the tools in your product-making arsenal. Little by little, you’ll be able to design, code, and sell faster and more efficiently. **尽早确定目标可以帮助你摆脱诱惑,防止你到最后忘记初心。** 也许你的项目并不能成为下一个 Snapchat, 但是在通往你目标的道路上,你会更加的熟练运用各种做产品的工具。渐渐地,你会拥有越来越强大的设计、代码、销售等的能力。 -### 2. Stay on track +### 2. Stay on track执行计划 -Sometimes product makers fail to ship because they lose motivation. **Set small goals; meeting those goals will help you build momentum.** Small wins will compound. +Sometimes product makers fail to ship because they lose motivation. **Set small goals; meeting those goals will help you build momentum.** Small wins will compound. 有一些产品没能完的原因是作者在半路失去了动力。 **设定一些小目标(比如先赚一个亿);完成这些目标会让你获得前进的动力。** 小的成就累计起来也是很了不起的。 ![](https://cdn-images-1.medium.com/max/800/1*ESildSVTSSOnFXGDxD-l9w.png) -Another thing you can do to stay on track is to be more public about your work. Share your progress online and with your peers. People love to see things like wireframes and will often times provide valuable input. +Another thing you can do to stay on track is to be more public about your work. Share your progress online and with your peers. People love to see things like wireframes and will often times provide valuable input.另一个让你保持动力的建议就是让你的项目更加公开。在网上和你的同行们分享你的进展。人们很喜欢看到线框(wireframe 翻译成 ”框架“ 会不会好一点?)类的东西,并常常带来有价值的投入。 -One trick that I find particularly motivating is making bets with friends. I tell them, if I don’t complete a task by a deadline, I’ll give them a hundred dollars. For example, I might set my task to getting twenty people to pay me within two weeks of launching a project. +One trick that I find particularly motivating is making bets with friends. I tell them, if I don’t complete a task by a deadline, I’ll give them a hundred dollars. For example, I might set my task to getting twenty people to pay me within two weeks of launching a project.我自己找到的一个非常有用的保持动力的小秘诀是和朋友打赌。我对他们说,如果我在截止日期前没有完成某个任务,我就给他们一百刀。比如,我会在一个项目开始的两周内找到二十个人打赌。 -### 3. Solve a problem +### 3. Solve a problem 解决一个问题 -Start by solving a problem. If you have experienced the problem yourself, even better. Ask your friends what kind of problems they experience. +Start by solving a problem. If you have experienced the problem yourself, even better. Ask your friends what kind of problems they experience.解决一个问题。如果你自己也经历了这个问题那就再好不过了。问问你的朋友们他们都有哪些问题需要解决。 -For example, I built [YC Careers](http://jonathanzwhite.github.io/yc-careers/) and [AtomSpace](https://atomspace.co/) to address the needs of one my friends who was having trouble applying to jobs and interviewing as a product designer. Overnight, YC Careers made it to the top of [ProductHunt](https://www.producthunt.com/posts/yc-careers) and AtomSpace booked $100 dollars from strangers within 6 hours of going live. +For example, I built [YC Careers](http://jonathanzwhite.github.io/yc-careers/) and [AtomSpace](https://atomspace.co/) to address the needs of one my friends who was having trouble applying to jobs and interviewing as a product designer. Overnight, YC Careers made it to the top of [ProductHunt](https://www.producthunt.com/posts/yc-careers) and AtomSpace booked $100 dollars from strangers within 6 hours of going live.比如,我做了 [YC Careers](http://jonathanzwhite.github.io/yc-careers/) 和 [AtomSpace](https://atomspace.co/) 来解决一个产品设计师朋友找工作和面试的烦恼。一夜之间,YC Careers 一下子爬到了 [ProductHunt](https://www.producthunt.com/posts/yc-careers) 的榜首,AtomSpace 在上线6个小时就拿到了来自陌生人的一百美元订单。 -**The bigger the pain point you’re trying to solve, the easier it will be to find users.** +**The bigger the pain point you’re trying to solve, the easier it will be to find users.你尝试解决多大的痛点,找用户就有多容易。** -Also, analyze problem-solutions from multiple perspectives. Some successful ideas might seem like a solution without a problem. For example, Instagram doesn’t seem to solve any immediate problem. But it does. Instagram solves the need for friends to stay updated on one another. If this need is not met, then it becomes a problem. +Also, analyze problem-solutions from multiple perspectives. Some successful ideas might seem like a solution without a problem. For example, Instagram doesn’t seem to solve any immediate problem. But it does. Instagram solves the need for friends to stay updated on one another. If this need is not met, then it becomes a problem. 同时,尝试从过个角度分析问题和解决方案。有些成功的点子看起来并没有解决任何问题,但其实不然。比如,Instagram 看起来没有直接解决任何问题。其实不是。Instagram 满足了朋友之间实时社交的需求。如果这个需求不能得到满足,那才是个问题。 -For further reading, [John Carmack on Idea Generation](https://amasad.me/carmack) and [How to Get Startup Ideas](http://paulgraham.com/startupideas.html) are two great essays to get you started. +For further reading, [John Carmack on Idea Generation](https://amasad.me/carmack) and [How to Get Startup Ideas](http://paulgraham.com/startupideas.html) are two great essays to get you started. 想要了解更多的话,[John Carmack on Idea Generation](https://amasad.me/carmack) 和 [How to Get Startup Ideas](http://paulgraham.com/startupideas.html) 这两篇文章是非常好的开始。 -### 4. Ditch the “good idea / bad idea” mindset +### 4. Ditch the “good idea / bad idea” mindset 放弃”好主意/坏主意“的想法 -Once you’ve identified a problem and come up with a solution, you’ll probably ask yourself if your idea is good. +Once you’ve identified a problem and come up with a solution, you’ll probably ask yourself if your idea is good.当你明确了你想要解决的问题并有了一个解决方案的时候,你可能会问自己这是不是一个好的点子。 -**Plenty of seemingly bad ideas have turned out to be great businesses.** For example, no one wanted to invest in Airbnb. Brian Chesky, one of the co-founders of Airbnb, detailed their rejections in his essay [7 Rejections](https://medium.com/@bchesky/7-rejections-7d894cbaa084#.l8fdqlasz). +**Plenty of seemingly bad ideas have turned out to be great businesses.** For example, no one wanted to invest in Airbnb. Brian Chesky, one of the co-founders of Airbnb, detailed their rejections in his essay [7 Rejections](https://medium.com/@bchesky/7-rejections-7d894cbaa084#.l8fdqlasz). **相当一部分看起来很糟糕的点子最后被证明是超级棒的生意。** 比如,多年没人想投资 Airbnb。 Brian Chesky, Airbnb 的创始人之一,在他的文章 [7 Rejections](https://medium.com/@bchesky/7-rejections-7d894cbaa084#.l8fdqlasz) 里详细讲述了被投资人拒绝的故事。 ![](https://cdn-images-1.medium.com/max/800/1*WpxUxMCO-7NXr-o1yo023g.png) -The only way to know for sure if your idea will work is to formulate a product hypothesis and run an experiment to test it. Talk to people and ask if they are interested in your solution. If you can get them to pay you before you’ve built anything, even better. +The only way to know for sure if your idea will work is to formulate a product hypothesis and run an experiment to test it. Talk to people and ask if they are interested in your solution. If you can get them to pay you before you’ve built anything, even better. 唯一验证你想法的办法就是做出一个产品来测试它。 找人聊聊,看看他们对你的解决方案是不是感兴趣。如果在你做出任何产品之前就有人原意为之付费那就再棒不过了。 -### 5. Ask for help +### 5. Ask for help寻求帮助 -When working on a project, reach out to people and ask for their help. **You will be surprised at how many people are willing to pay it forward and help a complete stranger.** +When working on a project, reach out to people and ask for their help. **You will be surprised at how many people are willing to pay it forward and help a complete stranger.** 当你在你的项目上埋头苦干的时候,记得寻求别人的帮助。 **你会很惊喜的发现竟然有这么多人原意去帮助一个陌生人(对,就是你)。** -Some of the best advice and feedback I have received came from reaching out to people with an email or a DM on Twitter. +Some of the best advice and feedback I have received came from reaching out to people with an email or a DM on Twitter. 我获得的最好的一部分建议就是来自于给别人发邮件或是推特(微博)私信。 -Also, when asking for help, be specific and prepare your questions. If you want feedback on a design, send the mockup. If you want feedback on your marketing strategy, detail what you have tried so far. Context is key. And if you don’t get a response, follow up politely. Sometimes the person you’re reaching out to might have genuinely missed your message. +Also, when asking for help, be specific and prepare your questions. If you want feedback on a design, send the mockup. If you want feedback on your marketing strategy, detail what you have tried so far. Context is key. And if you don’t get a response, follow up politely. Sometimes the person you’re reaching out to might have genuinely missed your message. 在寻求帮助的时候,请提一些精确、有价值的问题。如果你需要关于设计的一些反馈,就把草稿发给别人;如果你需要营销方面的建议,就详细的列出你尝试过的方法。内容才是关键。如果别人没有回复你,请礼貌的再次询问 —— 有时候别人只是没注意到你的消息而已。 --- -Build and launch your side projects. Use this process as a way to hone your craft so that when opportunity presents itself, you’re ready to take full advantage of it. Luck always favors the prepared. +Build and launch your side projects. Use this process as a way to hone your craft so that when opportunity presents itself, you’re ready to take full advantage of it. Luck always favors the prepared.开始你的兴趣项目吧。利用这个过程磨练自己,这样当机会来临的时候,你才能紧紧抓住。记住,机会总是青睐有准备的人。 -So next time you come up with an idea, act on it. Identify a problem, define your goal, stay on track, and ask for help. +So next time you come up with an idea, act on it. Identify a problem, define your goal, stay on track, and ask for help.所以下次你有一个想法的时候,采取行动吧。找到一个问题,确定你的目标,坚持下去,别忘了学会寻求帮助。 -What are you working on right now? How can I help you? Leave me a note here or [tweet](https://twitter.com/jonathanzwhite) them to me on Twitter. +What are you working on right now? How can I help you? Leave me a note here or [tweet](https://twitter.com/jonathanzwhite) them to me on Twitter.你现在在做神马项目呢?有什么是我能帮到你的吗?可以在这里或者我的 [tweet](https://twitter.com/jonathanzwhite) 下面留言。 -You can find me on Medium where I publish every week. Or you can follow me on [Twitter](https://twitter.com/JonathanZWhite), where I post non-sensical ramblings about design, front-end development, and virtual reality. +You can find me on Medium where I publish every week. Or you can follow me on [Twitter](https://twitter.com/JonathanZWhite), where I post non-sensical ramblings about design, front-end development, and virtual reality. 我在 Medium 上每周都会发文。也可以关注我的 [Twitter](https://twitter.com/JonathanZWhite),我会发一些关于设计、前端开发和虚拟现实的杂想。 -*P.S. If you enjoyed this article, it would mean a lot if you click the 💚 and share with friends.* +*P.S. If you enjoyed this article, it would mean a lot if you click the 💚 and share with friends.*(可删?) --- -> [掘金翻译计划](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)、[React](https://github.com/xitu/gold-miner#react)、[前端](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)。 \ No newline at end of file +> [掘金翻译计划](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)、[React](https://github.com/xitu/gold-miner#react)、[前端](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)。 From 784732d642d3393d58ce677312cb0e1f3854a8d8 Mon Sep 17 00:00:00 2001 From: Wenlin Ou Date: Fri, 7 Apr 2017 22:06:28 -0400 Subject: [PATCH 073/638] ... --- ...ng-will-change-until-you-start-building.md | 780 ++++++++++++++++-- 1 file changed, 733 insertions(+), 47 deletions(-) diff --git a/TODO/nothing-will-change-until-you-start-building.md b/TODO/nothing-will-change-until-you-start-building.md index 26cc605b437..df8d0872aa7 100644 --- a/TODO/nothing-will-change-until-you-start-building.md +++ b/TODO/nothing-will-change-until-you-start-building.md @@ -1,90 +1,776 @@ -> * 原文地址:[Nothing will change until you start building.](https://medium.freecodecamp.com/nothing-will-change-until-you-start-building-2681e85e7bdc) -> * 原文作者:[Jonathan Z. White](https://medium.freecodecamp.com/@JonathanZWhite?source=post_header_lockup) -> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 译者:[owenlyn](https://github.com/owenlyn) -> * 校对者: ---- -![](https://cdn-images-1.medium.com/max/1000/1*EwHpnCZ70FtJMi-lNSl-9Q.png) -# Nothing will change until you start building. 真正行动之前 你将一无所成 -This past week, I was in a Lyft. My driver was telling me about all of her ideas for side projects. She had ideas for a children’s book, an app that helps people find parking, and a more efficient way to package gifts. The problem was she was frozen by indecision. She had the ideas but she didn’t know where to start.上周我打了一辆 Lyft 出租车,司机跟我聊了很多关于她的天花乱坠的想法。她的想法里,有一个关于儿童的书籍,有帮助人们停车的APP、有一个更高效的打包礼物的方法,但问题是她总是犹豫不决:她又想法但却不知道从何开始。 -This is what I told her. **Start by building. Pick one project and do whatever you have to do to ship it.** If you want to write a book, start with writing a page a day. If you want to build an app, start with some sketches. Anyone can do it. 接下来这段话是我对她说的:**开始一些实际行动吧。选一个项目然后尽可能的去完成它。** 如果你想写一本书,那么从每天写一页开始;如果你像做一个APP,那就从草稿开始。任何人都可以做到这些。 -This advice applies to all creators. Once you start building and launching your projects, you won’t be able to stop. Building will become part of your identity. And even if your project fails, you’ll keep at it. 这些建议对所有创新者都适用。一旦你开始着手在你的项目上,你将会不由自主的继续做下去。创新将会成为你人格的一部分 —— 即使你的项目失败了。 + + + + -The steps you take today will compound over time. Look at great product makers like [Drew Wilson](https://twitter.com/drewwilson), [Pieter Levels](https://twitter.com/levelsio?), and [Sebastian Dobrincu](https://twitter.com/Sebyddd). **They don’t wait for the right opportunity.** They build and ship weekly. 你今天迈出的一小步将会是你人生的一大步。看看那些伟大的产品创造者吧,比如 [Drew Wilson](https://twitter.com/drewwilson), [Pieter Levels](https://twitter.com/levelsio?), 和 [Sebastian Dobrincu](https://twitter.com/Sebyddd)。**他们不仅仅是等待合适的时机。** 他们每周都在更新他们的产品。 -In an interview on [IndieHackers](https://www.indiehackers.com/podcast/006-josh-pigford-of-baremetrics), [Josh Pigford](https://twitter.com/Shpigford) disclosed that before he built Baremetrics, he had iterated through a *hundred* failed ideas. Today, Baremetrics makes over 60,000 in monthly recurring revenue. 在一个 [IndieHackers](https://www.indiehackers.com/podcast/006-josh-pigford-of-baremetrics) 的节目中, [Josh Pigford](https://twitter.com/Shpigford) 透露,在做 Baremetrics 之前,他已经尝试过 *一百个* 失败的点子。 现在, Baremetrics 每个月都会有 60,000 美金进账。 + + + + + + -![](https://cdn-images-1.medium.com/max/800/1*BzmVaqAKEzNRybUmMYOxdA.png) + + + gold-miner/nothing-will-change-until-you-start-building.md at master · xitu/gold-miner + + + -With that in mind, here are some tactics I’ve compiled from friends and acquaintances who are successful product makers.记住这些,接下来的要说的,是我总结的一些来自成功的产品创作者朋友们的策略。 + + ---- + + + + + + -### 1. Define your goal确定目标 + -Early on, figure out the main goal of your project. If you want to break out of your usual design style, focus on the design. If you want to test a new framework for your front-end, focus on the code. If you want to acquire a hundred users, focus on selling.在开始的时候,确定你的项目要实现的主要目标。如果你想打破你以往的设计风格,那就把心思花在设计上;如果你测试一个新的前端框架,那就把钻研代码;如果你想获得上百个用户(”大量用户“ 会不会好点),就把精力用在销售上。 + + + -**Define your goal early so you don’t get distracted and end up not achieving what you set out to do.** Perhaps your project doesn’t become the next Snapchat, but by working towards your goal you will have honed the tools in your product-making arsenal. Little by little, you’ll be able to design, code, and sell faster and more efficiently. **尽早确定目标可以帮助你摆脱诱惑,防止你到最后忘记初心。** 也许你的项目并不能成为下一个 Snapchat, 但是在通往你目标的道路上,你会更加的熟练运用各种做产品的工具。渐渐地,你会拥有越来越强大的设计、代码、销售等的能力。 + + -### 2. Stay on track执行计划 -Sometimes product makers fail to ship because they lose motivation. **Set small goals; meeting those goals will help you build momentum.** Small wins will compound. 有一些产品没能完的原因是作者在半路失去了动力。 **设定一些小目标(比如先赚一个亿);完成这些目标会让你获得前进的动力。** 小的成就累计起来也是很了不起的。 -![](https://cdn-images-1.medium.com/max/800/1*ESildSVTSSOnFXGDxD-l9w.png) -Another thing you can do to stay on track is to be more public about your work. Share your progress online and with your peers. People love to see things like wireframes and will often times provide valuable input.另一个让你保持动力的建议就是让你的项目更加公开。在网上和你的同行们分享你的进展。人们很喜欢看到线框(wireframe 翻译成 ”框架“ 会不会好一点?)类的东西,并常常带来有价值的投入。 + -One trick that I find particularly motivating is making bets with friends. I tell them, if I don’t complete a task by a deadline, I’ll give them a hundred dollars. For example, I might set my task to getting twenty people to pay me within two weeks of launching a project.我自己找到的一个非常有用的保持动力的小秘诀是和朋友打赌。我对他们说,如果我在截止日期前没有完成某个任务,我就给他们一百刀。比如,我会在一个项目开始的两周内找到二十个人打赌。 -### 3. Solve a problem 解决一个问题 + -Start by solving a problem. If you have experienced the problem yourself, even better. Ask your friends what kind of problems they experience.解决一个问题。如果你自己也经历了这个问题那就再好不过了。问问你的朋友们他们都有哪些问题需要解决。 + + -For example, I built [YC Careers](http://jonathanzwhite.github.io/yc-careers/) and [AtomSpace](https://atomspace.co/) to address the needs of one my friends who was having trouble applying to jobs and interviewing as a product designer. Overnight, YC Careers made it to the top of [ProductHunt](https://www.producthunt.com/posts/yc-careers) and AtomSpace booked $100 dollars from strangers within 6 hours of going live.比如,我做了 [YC Careers](http://jonathanzwhite.github.io/yc-careers/) 和 [AtomSpace](https://atomspace.co/) 来解决一个产品设计师朋友找工作和面试的烦恼。一夜之间,YC Careers 一下子爬到了 [ProductHunt](https://www.producthunt.com/posts/yc-careers) 的榜首,AtomSpace 在上线6个小时就拿到了来自陌生人的一百美元订单。 + + -**The bigger the pain point you’re trying to solve, the easier it will be to find users.你尝试解决多大的痛点,找用户就有多容易。** -Also, analyze problem-solutions from multiple perspectives. Some successful ideas might seem like a solution without a problem. For example, Instagram doesn’t seem to solve any immediate problem. But it does. Instagram solves the need for friends to stay updated on one another. If this need is not met, then it becomes a problem. 同时,尝试从过个角度分析问题和解决方案。有些成功的点子看起来并没有解决任何问题,但其实不然。比如,Instagram 看起来没有直接解决任何问题。其实不是。Instagram 满足了朋友之间实时社交的需求。如果这个需求不能得到满足,那才是个问题。 + -For further reading, [John Carmack on Idea Generation](https://amasad.me/carmack) and [How to Get Startup Ideas](http://paulgraham.com/startupideas.html) are two great essays to get you started. 想要了解更多的话,[John Carmack on Idea Generation](https://amasad.me/carmack) 和 [How to Get Startup Ideas](http://paulgraham.com/startupideas.html) 这两篇文章是非常好的开始。 + + -### 4. Ditch the “good idea / bad idea” mindset 放弃”好主意/坏主意“的想法 + + + -Once you’ve identified a problem and come up with a solution, you’ll probably ask yourself if your idea is good.当你明确了你想要解决的问题并有了一个解决方案的时候,你可能会问自己这是不是一个好的点子。 + + -**Plenty of seemingly bad ideas have turned out to be great businesses.** For example, no one wanted to invest in Airbnb. Brian Chesky, one of the co-founders of Airbnb, detailed their rejections in his essay [7 Rejections](https://medium.com/@bchesky/7-rejections-7d894cbaa084#.l8fdqlasz). **相当一部分看起来很糟糕的点子最后被证明是超级棒的生意。** 比如,多年没人想投资 Airbnb。 Brian Chesky, Airbnb 的创始人之一,在他的文章 [7 Rejections](https://medium.com/@bchesky/7-rejections-7d894cbaa084#.l8fdqlasz) 里详细讲述了被投资人拒绝的故事。 -![](https://cdn-images-1.medium.com/max/800/1*WpxUxMCO-7NXr-o1yo023g.png) + -The only way to know for sure if your idea will work is to formulate a product hypothesis and run an experiment to test it. Talk to people and ask if they are interested in your solution. If you can get them to pay you before you’ve built anything, even better. 唯一验证你想法的办法就是做出一个产品来测试它。 找人聊聊,看看他们对你的解决方案是不是感兴趣。如果在你做出任何产品之前就有人原意为之付费那就再棒不过了。 -### 5. Ask for help寻求帮助 + -When working on a project, reach out to people and ask for their help. **You will be surprised at how many people are willing to pay it forward and help a complete stranger.** 当你在你的项目上埋头苦干的时候,记得寻求别人的帮助。 **你会很惊喜的发现竟然有这么多人原意去帮助一个陌生人(对,就是你)。** + -Some of the best advice and feedback I have received came from reaching out to people with an email or a DM on Twitter. 我获得的最好的一部分建议就是来自于给别人发邮件或是推特(微博)私信。 + + -Also, when asking for help, be specific and prepare your questions. If you want feedback on a design, send the mockup. If you want feedback on your marketing strategy, detail what you have tried so far. Context is key. And if you don’t get a response, follow up politely. Sometimes the person you’re reaching out to might have genuinely missed your message. 在寻求帮助的时候,请提一些精确、有价值的问题。如果你需要关于设计的一些反馈,就把草稿发给别人;如果你需要营销方面的建议,就详细的列出你尝试过的方法。内容才是关键。如果别人没有回复你,请礼貌的再次询问 —— 有时候别人只是没注意到你的消息而已。 + ---- -Build and launch your side projects. Use this process as a way to hone your craft so that when opportunity presents itself, you’re ready to take full advantage of it. Luck always favors the prepared.开始你的兴趣项目吧。利用这个过程磨练自己,这样当机会来临的时候,你才能紧紧抓住。记住,机会总是青睐有准备的人。 + -So next time you come up with an idea, act on it. Identify a problem, define your goal, stay on track, and ask for help.所以下次你有一个想法的时候,采取行动吧。找到一个问题,确定你的目标,坚持下去,别忘了学会寻求帮助。 + -What are you working on right now? How can I help you? Leave me a note here or [tweet](https://twitter.com/jonathanzwhite) them to me on Twitter.你现在在做神马项目呢?有什么是我能帮到你的吗?可以在这里或者我的 [tweet](https://twitter.com/jonathanzwhite) 下面留言。 + + -You can find me on Medium where I publish every week. Or you can follow me on [Twitter](https://twitter.com/JonathanZWhite), where I post non-sensical ramblings about design, front-end development, and virtual reality. 我在 Medium 上每周都会发文。也可以关注我的 [Twitter](https://twitter.com/JonathanZWhite),我会发一些关于设计、前端开发和虚拟现实的杂想。 -*P.S. If you enjoyed this article, it would mean a lot if you click the 💚 and share with friends.*(可删?) +
+ Skip to content +
---- + + + + + + + + + + + + +
+ +
+ +
+
+ + + +
+
+
+ + + + +
+
+ + +
    +
  • +
    + +
    + + + + Watch + + + + +
    +
    +
    + + Notifications +
    + + + +
    +
    +
    +
    +
  • + +
  • +
    +
    + + +
    +
    + + +
    + +
  • + +
  • + + + Fork + + + + + +
  • +
+ +

+ + /gold-miner + +

+ +
+ +
+ +
+
+ + + + +Permalink + + + +
+ +
+ + +
+ +
+
+ + Switch branches/tags +
+ +
+
+ +
+
+ +
+
+ + + +
+
+ + +
+ +
Nothing to show
+
+ +
+
+
+ +
+ + Find file + + +
+ +
+ + + +
+ + + e1c20be + + Mar 30, 2017 + + + +
+ + +
+ + +
+ +
+
+
+ +
+ Raw + Blame + History +
+ + + + + +
+ +
+ +
+ +
+ 91 lines (47 sloc) + + 7.52 KB +
+
+ + +
+
+ +
+
+

+

Nothing will change until you start building.

+

This past week, I was in a Lyft. My driver was telling me about all of her ideas for side projects. She had ideas for a children’s book, an app that helps people find parking, and a more efficient way to package gifts. The problem was she was frozen by indecision. She had the ideas but she didn’t know where to start.

+

This is what I told her. Start by building. Pick one project and do whatever you have to do to ship it. If you want to write a book, start with writing a page a day. If you want to build an app, start with some sketches. Anyone can do it.

+

This advice applies to all creators. Once you start building and launching your projects, you won’t be able to stop. Building will become part of your identity. And even if your project fails, you’ll keep at it.

+

The steps you take today will compound over time. Look at great product makers like Drew Wilson, Pieter Levels, and Sebastian Dobrincu. They don’t wait for the right opportunity. They build and ship weekly.

+

In an interview on IndieHackers, Josh Pigford disclosed that before he built Baremetrics, he had iterated through a hundred failed ideas. Today, Baremetrics makes over 60,000 in monthly recurring revenue.

+

+

With that in mind, here are some tactics I’ve compiled from friends and acquaintances who are successful product makers.

+
+

1. Define your goal

+

Early on, figure out the main goal of your project. If you want to break out of your usual design style, focus on the design. If you want to test a new framework for your front-end, focus on the code. If you want to acquire a hundred users, focus on selling.

+

Define your goal early so you don’t get distracted and end up not achieving what you set out to do. Perhaps your project doesn’t become the next Snapchat, but by working towards your goal you will have honed the tools in your product-making arsenal. Little by little, you’ll be able to design, code, and sell faster and more efficiently.

+

2. Stay on track

+

Sometimes product makers fail to ship because they lose motivation. Set small goals; meeting those goals will help you build momentum. Small wins will compound.

+

+

Another thing you can do to stay on track is to be more public about your work. Share your progress online and with your peers. People love to see things like wireframes and will often times provide valuable input.

+

One trick that I find particularly motivating is making bets with friends. I tell them, if I don’t complete a task by a deadline, I’ll give them a hundred dollars. For example, I might set my task to getting twenty people to pay me within two weeks of launching a project.

+

3. Solve a problem

+

Start by solving a problem. If you have experienced the problem yourself, even better. Ask your friends what kind of problems they experience.

+

For example, I built YC Careers and AtomSpace to address the needs of one my friends who was having trouble applying to jobs and interviewing as a product designer. Overnight, YC Careers made it to the top of ProductHunt and AtomSpace booked $100 dollars from strangers within 6 hours of going live.

+

The bigger the pain point you’re trying to solve, the easier it will be to find users.

+

Also, analyze problem-solutions from multiple perspectives. Some successful ideas might seem like a solution without a problem. For example, Instagram doesn’t seem to solve any immediate problem. But it does. Instagram solves the need for friends to stay updated on one another. If this need is not met, then it becomes a problem.

+

For further reading, John Carmack on Idea Generation and How to Get Startup Ideas are two great essays to get you started.

+

4. Ditch the “good idea / bad idea” mindset

+

Once you’ve identified a problem and come up with a solution, you’ll probably ask yourself if your idea is good.

+

Plenty of seemingly bad ideas have turned out to be great businesses. For example, no one wanted to invest in Airbnb. Brian Chesky, one of the co-founders of Airbnb, detailed their rejections in his essay 7 Rejections.

+

+

The only way to know for sure if your idea will work is to formulate a product hypothesis and run an experiment to test it. Talk to people and ask if they are interested in your solution. If you can get them to pay you before you’ve built anything, even better.

+

5. Ask for help

+

When working on a project, reach out to people and ask for their help. You will be surprised at how many people are willing to pay it forward and help a complete stranger.

+

Some of the best advice and feedback I have received came from reaching out to people with an email or a DM on Twitter.

+

Also, when asking for help, be specific and prepare your questions. If you want feedback on a design, send the mockup. If you want feedback on your marketing strategy, detail what you have tried so far. Context is key. And if you don’t get a response, follow up politely. Sometimes the person you’re reaching out to might have genuinely missed your message.

+
+

Build and launch your side projects. Use this process as a way to hone your craft so that when opportunity presents itself, you’re ready to take full advantage of it. Luck always favors the prepared.

+

So next time you come up with an idea, act on it. Identify a problem, define your goal, stay on track, and ask for help.

+

What are you working on right now? How can I help you? Leave me a note here or tweet them to me on Twitter.

+

You can find me on Medium where I publish every week. Or you can follow me on Twitter, where I post non-sensical ramblings about design, front-end development, and virtual reality.

+

P.S. If you enjoyed this article, it would mean a lot if you click the 💚 and share with friends.

+
+
+

掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOSReact前端后端产品设计 等领域,想要查看更多优质译文请持续关注 掘金翻译计划

+
+
+
+ +
+ + + + + +
+ +
+ +
+
+ +
+ + + + + + + +
+ + + You can't perform that action at this time. +
+ + + + + + + + + +
+ + You signed in with another tab or window. Reload to refresh your session. + You signed out in another tab or window. Reload to refresh your session. +
+ + + + + -> [掘金翻译计划](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)、[React](https://github.com/xitu/gold-miner#react)、[前端](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)。 From c61a1606f3499108f7170f24c3937f5428968021 Mon Sep 17 00:00:00 2001 From: Wenlin Ou Date: Fri, 7 Apr 2017 22:07:53 -0400 Subject: [PATCH 074/638] ... --- ...ng-will-change-until-you-start-building.md | 780 ++---------------- 1 file changed, 47 insertions(+), 733 deletions(-) diff --git a/TODO/nothing-will-change-until-you-start-building.md b/TODO/nothing-will-change-until-you-start-building.md index df8d0872aa7..0d57984107d 100644 --- a/TODO/nothing-will-change-until-you-start-building.md +++ b/TODO/nothing-will-change-until-you-start-building.md @@ -1,776 +1,90 @@ +> * 原文地址:[Nothing will change until you start building.](https://medium.freecodecamp.com/nothing-will-change-until-you-start-building-2681e85e7bdc) +> * 原文作者:[Jonathan Z. White](https://medium.freecodecamp.com/@JonathanZWhite?source=post_header_lockup) +> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) +> * 译者: +> * 校对者: +--- +![](https://cdn-images-1.medium.com/max/1000/1*EwHpnCZ70FtJMi-lNSl-9Q.png) +# Nothing will change until you start building. +This past week, I was in a Lyft. My driver was telling me about all of her ideas for side projects. She had ideas for a children’s book, an app that helps people find parking, and a more efficient way to package gifts. The problem was she was frozen by indecision. She had the ideas but she didn’t know where to start. +This is what I told her. **Start by building. Pick one project and do whatever you have to do to ship it.** If you want to write a book, start with writing a page a day. If you want to build an app, start with some sketches. Anyone can do it. - - - - +This advice applies to all creators. Once you start building and launching your projects, you won’t be able to stop. Building will become part of your identity. And even if your project fails, you’ll keep at it. +The steps you take today will compound over time. Look at great product makers like [Drew Wilson](https://twitter.com/drewwilson), [Pieter Levels](https://twitter.com/levelsio?), and [Sebastian Dobrincu](https://twitter.com/Sebyddd). **They don’t wait for the right opportunity.** They build and ship weekly. +In an interview on [IndieHackers](https://www.indiehackers.com/podcast/006-josh-pigford-of-baremetrics), [Josh Pigford](https://twitter.com/Shpigford) disclosed that before he built Baremetrics, he had iterated through a *hundred* failed ideas. Today, Baremetrics makes over 60,000 in monthly recurring revenue. - - - - - - - - - gold-miner/nothing-will-change-until-you-start-building.md at master · xitu/gold-miner - - - +![](https://cdn-images-1.medium.com/max/800/1*BzmVaqAKEzNRybUmMYOxdA.png) - - +With that in mind, here are some tactics I’ve compiled from friends and acquaintances who are successful product makers. - - - - - - +--- - +### 1. Define your goal - - - +Early on, figure out the main goal of your project. If you want to break out of your usual design style, focus on the design. If you want to test a new framework for your front-end, focus on the code. If you want to acquire a hundred users, focus on selling. - - +**Define your goal early so you don’t get distracted and end up not achieving what you set out to do.** Perhaps your project doesn’t become the next Snapchat, but by working towards your goal you will have honed the tools in your product-making arsenal. Little by little, you’ll be able to design, code, and sell faster and more efficiently. +### 2. Stay on track +Sometimes product makers fail to ship because they lose motivation. **Set small goals; meeting those goals will help you build momentum.** Small wins will compound. +![](https://cdn-images-1.medium.com/max/800/1*ESildSVTSSOnFXGDxD-l9w.png) - +Another thing you can do to stay on track is to be more public about your work. Share your progress online and with your peers. People love to see things like wireframes and will often times provide valuable input. +One trick that I find particularly motivating is making bets with friends. I tell them, if I don’t complete a task by a deadline, I’ll give them a hundred dollars. For example, I might set my task to getting twenty people to pay me within two weeks of launching a project. - +### 3. Solve a problem - - +Start by solving a problem. If you have experienced the problem yourself, even better. Ask your friends what kind of problems they experience. - - +For example, I built [YC Careers](http://jonathanzwhite.github.io/yc-careers/) and [AtomSpace](https://atomspace.co/) to address the needs of one my friends who was having trouble applying to jobs and interviewing as a product designer. Overnight, YC Careers made it to the top of [ProductHunt](https://www.producthunt.com/posts/yc-careers) and AtomSpace booked $100 dollars from strangers within 6 hours of going live. +**The bigger the pain point you’re trying to solve, the easier it will be to find users.** - +Also, analyze problem-solutions from multiple perspectives. Some successful ideas might seem like a solution without a problem. For example, Instagram doesn’t seem to solve any immediate problem. But it does. Instagram solves the need for friends to stay updated on one another. If this need is not met, then it becomes a problem. - - +For further reading, [John Carmack on Idea Generation](https://amasad.me/carmack) and [How to Get Startup Ideas](http://paulgraham.com/startupideas.html) are two great essays to get you started. - - - +### 4. Ditch the “good idea / bad idea” mindset - - +Once you’ve identified a problem and come up with a solution, you’ll probably ask yourself if your idea is good. +**Plenty of seemingly bad ideas have turned out to be great businesses.** For example, no one wanted to invest in Airbnb. Brian Chesky, one of the co-founders of Airbnb, detailed their rejections in his essay [7 Rejections](https://medium.com/@bchesky/7-rejections-7d894cbaa084#.l8fdqlasz). - +![](https://cdn-images-1.medium.com/max/800/1*WpxUxMCO-7NXr-o1yo023g.png) +The only way to know for sure if your idea will work is to formulate a product hypothesis and run an experiment to test it. Talk to people and ask if they are interested in your solution. If you can get them to pay you before you’ve built anything, even better. - +### 5. Ask for help - +When working on a project, reach out to people and ask for their help. **You will be surprised at how many people are willing to pay it forward and help a complete stranger.** - - +Some of the best advice and feedback I have received came from reaching out to people with an email or a DM on Twitter. - +Also, when asking for help, be specific and prepare your questions. If you want feedback on a design, send the mockup. If you want feedback on your marketing strategy, detail what you have tried so far. Context is key. And if you don’t get a response, follow up politely. Sometimes the person you’re reaching out to might have genuinely missed your message. +--- - +Build and launch your side projects. Use this process as a way to hone your craft so that when opportunity presents itself, you’re ready to take full advantage of it. Luck always favors the prepared. - +So next time you come up with an idea, act on it. Identify a problem, define your goal, stay on track, and ask for help. - - +What are you working on right now? How can I help you? Leave me a note here or [tweet](https://twitter.com/jonathanzwhite) them to me on Twitter. +You can find me on Medium where I publish every week. Or you can follow me on [Twitter](https://twitter.com/JonathanZWhite), where I post non-sensical ramblings about design, front-end development, and virtual reality. -
- Skip to content -
+*P.S. If you enjoyed this article, it would mean a lot if you click the 💚 and share with friends.* - - - - - - - - - - - - -
- -
- -
-
- - - -
-
-
- - - - -
-
- - -
    -
  • -
    - -
    - - - - Watch - - - - -
    -
    -
    - - Notifications -
    - - - -
    -
    -
    -
    -
  • - -
  • -
    -
    - - -
    -
    - - -
    - -
  • - -
  • - - - Fork - - - - - -
  • -
- -

- - /gold-miner - -

- -
- -
- -
-
- - - - -Permalink - - - -
- -
- - -
- -
-
- - Switch branches/tags -
- -
-
- -
-
- -
-
- - - -
-
- - -
- -
Nothing to show
-
- -
-
-
- -
- - Find file - - -
- -
- - - -
- - - e1c20be - - Mar 30, 2017 - - - -
- - -
- - -
- -
-
-
- -
- Raw - Blame - History -
- - - - - -
- -
- -
- -
- 91 lines (47 sloc) - - 7.52 KB -
-
- - -
-
- -
-
-

-

Nothing will change until you start building.

-

This past week, I was in a Lyft. My driver was telling me about all of her ideas for side projects. She had ideas for a children’s book, an app that helps people find parking, and a more efficient way to package gifts. The problem was she was frozen by indecision. She had the ideas but she didn’t know where to start.

-

This is what I told her. Start by building. Pick one project and do whatever you have to do to ship it. If you want to write a book, start with writing a page a day. If you want to build an app, start with some sketches. Anyone can do it.

-

This advice applies to all creators. Once you start building and launching your projects, you won’t be able to stop. Building will become part of your identity. And even if your project fails, you’ll keep at it.

-

The steps you take today will compound over time. Look at great product makers like Drew Wilson, Pieter Levels, and Sebastian Dobrincu. They don’t wait for the right opportunity. They build and ship weekly.

-

In an interview on IndieHackers, Josh Pigford disclosed that before he built Baremetrics, he had iterated through a hundred failed ideas. Today, Baremetrics makes over 60,000 in monthly recurring revenue.

-

-

With that in mind, here are some tactics I’ve compiled from friends and acquaintances who are successful product makers.

-
-

1. Define your goal

-

Early on, figure out the main goal of your project. If you want to break out of your usual design style, focus on the design. If you want to test a new framework for your front-end, focus on the code. If you want to acquire a hundred users, focus on selling.

-

Define your goal early so you don’t get distracted and end up not achieving what you set out to do. Perhaps your project doesn’t become the next Snapchat, but by working towards your goal you will have honed the tools in your product-making arsenal. Little by little, you’ll be able to design, code, and sell faster and more efficiently.

-

2. Stay on track

-

Sometimes product makers fail to ship because they lose motivation. Set small goals; meeting those goals will help you build momentum. Small wins will compound.

-

-

Another thing you can do to stay on track is to be more public about your work. Share your progress online and with your peers. People love to see things like wireframes and will often times provide valuable input.

-

One trick that I find particularly motivating is making bets with friends. I tell them, if I don’t complete a task by a deadline, I’ll give them a hundred dollars. For example, I might set my task to getting twenty people to pay me within two weeks of launching a project.

-

3. Solve a problem

-

Start by solving a problem. If you have experienced the problem yourself, even better. Ask your friends what kind of problems they experience.

-

For example, I built YC Careers and AtomSpace to address the needs of one my friends who was having trouble applying to jobs and interviewing as a product designer. Overnight, YC Careers made it to the top of ProductHunt and AtomSpace booked $100 dollars from strangers within 6 hours of going live.

-

The bigger the pain point you’re trying to solve, the easier it will be to find users.

-

Also, analyze problem-solutions from multiple perspectives. Some successful ideas might seem like a solution without a problem. For example, Instagram doesn’t seem to solve any immediate problem. But it does. Instagram solves the need for friends to stay updated on one another. If this need is not met, then it becomes a problem.

-

For further reading, John Carmack on Idea Generation and How to Get Startup Ideas are two great essays to get you started.

-

4. Ditch the “good idea / bad idea” mindset

-

Once you’ve identified a problem and come up with a solution, you’ll probably ask yourself if your idea is good.

-

Plenty of seemingly bad ideas have turned out to be great businesses. For example, no one wanted to invest in Airbnb. Brian Chesky, one of the co-founders of Airbnb, detailed their rejections in his essay 7 Rejections.

-

-

The only way to know for sure if your idea will work is to formulate a product hypothesis and run an experiment to test it. Talk to people and ask if they are interested in your solution. If you can get them to pay you before you’ve built anything, even better.

-

5. Ask for help

-

When working on a project, reach out to people and ask for their help. You will be surprised at how many people are willing to pay it forward and help a complete stranger.

-

Some of the best advice and feedback I have received came from reaching out to people with an email or a DM on Twitter.

-

Also, when asking for help, be specific and prepare your questions. If you want feedback on a design, send the mockup. If you want feedback on your marketing strategy, detail what you have tried so far. Context is key. And if you don’t get a response, follow up politely. Sometimes the person you’re reaching out to might have genuinely missed your message.

-
-

Build and launch your side projects. Use this process as a way to hone your craft so that when opportunity presents itself, you’re ready to take full advantage of it. Luck always favors the prepared.

-

So next time you come up with an idea, act on it. Identify a problem, define your goal, stay on track, and ask for help.

-

What are you working on right now? How can I help you? Leave me a note here or tweet them to me on Twitter.

-

You can find me on Medium where I publish every week. Or you can follow me on Twitter, where I post non-sensical ramblings about design, front-end development, and virtual reality.

-

P.S. If you enjoyed this article, it would mean a lot if you click the 💚 and share with friends.

-
-
-

掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOSReact前端后端产品设计 等领域,想要查看更多优质译文请持续关注 掘金翻译计划

-
-
-
- -
- - - - - -
- -
- -
-
- -
- - - - - - - -
- - - You can't perform that action at this time. -
- - - - - - - - - -
- - You signed in with another tab or window. Reload to refresh your session. - You signed out in another tab or window. Reload to refresh your session. -
- - - - - +--- +> [掘金翻译计划](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)、[React](https://github.com/xitu/gold-miner#react)、[前端](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)。 \ No newline at end of file From f4857dd5c82e2387af2c9ffbd88d767f974a0866 Mon Sep 17 00:00:00 2001 From: Wenlin Ou Date: Fri, 7 Apr 2017 22:15:49 -0400 Subject: [PATCH 075/638] get ready for proofreading --- ...ng-will-change-until-you-start-building.md | 70 +++++++++---------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/TODO/nothing-will-change-until-you-start-building.md b/TODO/nothing-will-change-until-you-start-building.md index 0d57984107d..7d9880eb6ba 100644 --- a/TODO/nothing-will-change-until-you-start-building.md +++ b/TODO/nothing-will-change-until-you-start-building.md @@ -1,90 +1,90 @@ > * 原文地址:[Nothing will change until you start building.](https://medium.freecodecamp.com/nothing-will-change-until-you-start-building-2681e85e7bdc) > * 原文作者:[Jonathan Z. White](https://medium.freecodecamp.com/@JonathanZWhite?source=post_header_lockup) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 译者: +> * 译者:[owenlyn](https://github.com/owenlyn) > * 校对者: --- ![](https://cdn-images-1.medium.com/max/1000/1*EwHpnCZ70FtJMi-lNSl-9Q.png) -# Nothing will change until you start building. +# 真正行动之前 你将一无所成 -This past week, I was in a Lyft. My driver was telling me about all of her ideas for side projects. She had ideas for a children’s book, an app that helps people find parking, and a more efficient way to package gifts. The problem was she was frozen by indecision. She had the ideas but she didn’t know where to start. +就在上周我打了一辆 Lyft 出租车,司机跟我聊了很多关于她的天花乱坠的想法。她的想法里,有一个关于儿童的书籍,有帮助人们停车的APP、有一个更高效的打包礼物的方法,但问题是她总是犹豫不决:她又想法但却不知道从何开始。 -This is what I told her. **Start by building. Pick one project and do whatever you have to do to ship it.** If you want to write a book, start with writing a page a day. If you want to build an app, start with some sketches. Anyone can do it. +接下来这段话是我对她说的:**开始一些实际行动吧。选一个项目然后尽可能的去完成它。** 如果你想写一本书,那么从每天写一页开始;如果你像做一个APP,那就从草稿开始。任何人都可以做到这些。 -This advice applies to all creators. Once you start building and launching your projects, you won’t be able to stop. Building will become part of your identity. And even if your project fails, you’ll keep at it. +这些建议对所有创新者都适用。一旦你开始着手在你的项目上,你将会不由自主的继续做下去。创新将会成为你人格的一部分 —— 即使你的项目失败了。 -The steps you take today will compound over time. Look at great product makers like [Drew Wilson](https://twitter.com/drewwilson), [Pieter Levels](https://twitter.com/levelsio?), and [Sebastian Dobrincu](https://twitter.com/Sebyddd). **They don’t wait for the right opportunity.** They build and ship weekly. +你今天迈出的一小步将会是你人生的一大步。看看那些伟大的产品创造者吧,比如 [Drew Wilson](https://twitter.com/drewwilson), [Pieter Levels](https://twitter.com/levelsio?), 和 [Sebastian Dobrincu](https://twitter.com/Sebyddd)。**他们不仅仅是等待合适的时机。** 他们每周都在更新他们的产品。 -In an interview on [IndieHackers](https://www.indiehackers.com/podcast/006-josh-pigford-of-baremetrics), [Josh Pigford](https://twitter.com/Shpigford) disclosed that before he built Baremetrics, he had iterated through a *hundred* failed ideas. Today, Baremetrics makes over 60,000 in monthly recurring revenue. +在一个 [IndieHackers](https://www.indiehackers.com/podcast/006-josh-pigford-of-baremetrics) 的节目中, [Josh Pigford](https://twitter.com/Shpigford) 透露,在做 Baremetrics 之前,他已经尝试过 *一百个* 失败的点子。 现在, Baremetrics 每个月都会有 60,000 美金进账。 ![](https://cdn-images-1.medium.com/max/800/1*BzmVaqAKEzNRybUmMYOxdA.png) -With that in mind, here are some tactics I’ve compiled from friends and acquaintances who are successful product makers. +记住这些,接下来的要说的,是我总结的一些来自成功的产品创作者朋友们的策略。 --- -### 1. Define your goal +### 1. 确定目标 -Early on, figure out the main goal of your project. If you want to break out of your usual design style, focus on the design. If you want to test a new framework for your front-end, focus on the code. If you want to acquire a hundred users, focus on selling. +在开始的时候,确定你的项目要实现的主要目标。如果你想打破你以往的设计风格,那就把心思花在设计上;如果你测试一个新的前端框架,那就把钻研代码;如果你想获得上百个用户(”大量用户“ 会不会好点),就把精力用在销售上。 -**Define your goal early so you don’t get distracted and end up not achieving what you set out to do.** Perhaps your project doesn’t become the next Snapchat, but by working towards your goal you will have honed the tools in your product-making arsenal. Little by little, you’ll be able to design, code, and sell faster and more efficiently. +**尽早确定目标可以帮助你摆脱诱惑,防止你到最后忘记初心。** 也许你的项目并不能成为下一个 Snapchat, 但是在通往你目标的道路上,你会更加的熟练运用各种做产品的工具。渐渐地,你会拥有越来越强大的设计、代码、销售等的能力。 -### 2. Stay on track +### 2. 执行计划 -Sometimes product makers fail to ship because they lose motivation. **Set small goals; meeting those goals will help you build momentum.** Small wins will compound. +有一些产品没能完的原因是作者在半路失去了动力。 **设定一些小目标(比如先赚一个亿);完成这些目标会让你获得前进的动力。** 小的成就累计起来也是很了不起的。 ![](https://cdn-images-1.medium.com/max/800/1*ESildSVTSSOnFXGDxD-l9w.png) -Another thing you can do to stay on track is to be more public about your work. Share your progress online and with your peers. People love to see things like wireframes and will often times provide valuable input. +另一个让你保持动力的建议就是让你的项目更加公开。在网上和你的同行们分享你的进展。人们很喜欢看到线框(wireframe 翻译成 ”框架“ 会不会好一点?)类的东西,并常常带来有价值的投入。 -One trick that I find particularly motivating is making bets with friends. I tell them, if I don’t complete a task by a deadline, I’ll give them a hundred dollars. For example, I might set my task to getting twenty people to pay me within two weeks of launching a project. +我自己找到的一个非常有用的保持动力的小秘诀是和朋友打赌。我对他们说,如果我在截止日期前没有完成某个任务,我就给他们一百刀。比如,我会在一个项目开始的两周内找到二十个人打赌。 -### 3. Solve a problem +### 3. 解决一个问题 -Start by solving a problem. If you have experienced the problem yourself, even better. Ask your friends what kind of problems they experience. +解决一个问题。如果你自己也经历了这个问题那就再好不过了。问问你的朋友们他们都有哪些问题需要解决。 -For example, I built [YC Careers](http://jonathanzwhite.github.io/yc-careers/) and [AtomSpace](https://atomspace.co/) to address the needs of one my friends who was having trouble applying to jobs and interviewing as a product designer. Overnight, YC Careers made it to the top of [ProductHunt](https://www.producthunt.com/posts/yc-careers) and AtomSpace booked $100 dollars from strangers within 6 hours of going live. +比如,我做了 [YC Careers](http://jonathanzwhite.github.io/yc-careers/) 和 [AtomSpace](https://atomspace.co/) 来解决一个产品设计师朋友找工作和面试的烦恼。一夜之间,YC Careers 一下子爬到了 [ProductHunt](https://www.producthunt.com/posts/yc-careers) 的榜首,AtomSpace 在上线6个小时就拿到了来自陌生人的一百美元订单。 -**The bigger the pain point you’re trying to solve, the easier it will be to find users.** +**你解决越大的痛点,就越容易找到用户。** -Also, analyze problem-solutions from multiple perspectives. Some successful ideas might seem like a solution without a problem. For example, Instagram doesn’t seem to solve any immediate problem. But it does. Instagram solves the need for friends to stay updated on one another. If this need is not met, then it becomes a problem. +同时,尝试从过个角度分析问题和解决方案。有些成功的点子看起来并没有解决任何问题,但其实不然。比如,Instagram 看起来没有直接解决任何问题。其实不是。Instagram 满足了朋友之间实时社交的需求。如果这个需求不能得到满足,那才是个问题。 -For further reading, [John Carmack on Idea Generation](https://amasad.me/carmack) and [How to Get Startup Ideas](http://paulgraham.com/startupideas.html) are two great essays to get you started. +想要了解更多的话,[John Carmack on Idea Generation](https://amasad.me/carmack) 和 [How to Get Startup Ideas](http://paulgraham.com/startupideas.html) 这两篇文章是非常好的开始。 -### 4. Ditch the “good idea / bad idea” mindset +### 4. 放弃”好主意/坏主意“的想法 -Once you’ve identified a problem and come up with a solution, you’ll probably ask yourself if your idea is good. +当你明确了你想要解决的问题并有了一个解决方案的时候,你可能会问自己这是不是一个好的点子。 -**Plenty of seemingly bad ideas have turned out to be great businesses.** For example, no one wanted to invest in Airbnb. Brian Chesky, one of the co-founders of Airbnb, detailed their rejections in his essay [7 Rejections](https://medium.com/@bchesky/7-rejections-7d894cbaa084#.l8fdqlasz). +**相当一部分看起来很糟糕的点子最后被证明是超级棒的生意。** 比如,多年没人想投资 Airbnb。 Brian Chesky, Airbnb 的创始人之一,在他的文章 [7 Rejections](https://medium.com/@bchesky/7-rejections-7d894cbaa084#.l8fdqlasz) 里详细讲述了被投资人拒绝的故事。 ![](https://cdn-images-1.medium.com/max/800/1*WpxUxMCO-7NXr-o1yo023g.png) -The only way to know for sure if your idea will work is to formulate a product hypothesis and run an experiment to test it. Talk to people and ask if they are interested in your solution. If you can get them to pay you before you’ve built anything, even better. +唯一验证你想法的办法就是做出一个产品来测试它。 找人聊聊,看看他们对你的解决方案是不是感兴趣。如果在你做出任何产品之前就有人原意为之付费那就再棒不过了。 -### 5. Ask for help +### 5. 寻求帮助 -When working on a project, reach out to people and ask for their help. **You will be surprised at how many people are willing to pay it forward and help a complete stranger.** +当你在你的项目上埋头苦干的时候,记得寻求别人的帮助。 **你会很惊喜的发现竟然有这么多人原意去帮助一个陌生人(对,就是你)。** -Some of the best advice and feedback I have received came from reaching out to people with an email or a DM on Twitter. +我获得的最好的一部分建议就是来自于给别人发邮件或是推特(微博)私信。 -Also, when asking for help, be specific and prepare your questions. If you want feedback on a design, send the mockup. If you want feedback on your marketing strategy, detail what you have tried so far. Context is key. And if you don’t get a response, follow up politely. Sometimes the person you’re reaching out to might have genuinely missed your message. +在寻求帮助的时候,请提一些精确、有价值的问题。如果你需要关于设计的一些反馈,就把草稿发给别人;如果你需要营销方面的建议,就详细的列出你尝试过的方法。内容才是关键。如果别人没有回复你,请礼貌的再次询问 —— 有时候别人只是没注意到你的消息而已。 --- -Build and launch your side projects. Use this process as a way to hone your craft so that when opportunity presents itself, you’re ready to take full advantage of it. Luck always favors the prepared. +开始你的兴趣项目吧。利用这个过程磨练自己,这样当机会来临的时候,你才能紧紧抓住。记住,机会总是青睐有准备的人。 -So next time you come up with an idea, act on it. Identify a problem, define your goal, stay on track, and ask for help. +所以下次你有一个想法的时候,采取行动吧。找到一个问题,确定你的目标,坚持下去,别忘了学会寻求帮助。 -What are you working on right now? How can I help you? Leave me a note here or [tweet](https://twitter.com/jonathanzwhite) them to me on Twitter. +你现在在做什么项目呢?有什么是我能帮到你的吗?可以在这里或者我的 [tweet](https://twitter.com/jonathanzwhite) 下面留言。 -You can find me on Medium where I publish every week. Or you can follow me on [Twitter](https://twitter.com/JonathanZWhite), where I post non-sensical ramblings about design, front-end development, and virtual reality. +我在 Medium 上每周都会发文。也可以关注我的 [Twitter](https://twitter.com/JonathanZWhite),我会发一些关于设计、前端开发和虚拟现实的杂想。 -*P.S. If you enjoyed this article, it would mean a lot if you click the 💚 and share with friends.* +*P.S. If you enjoyed this article, it would mean a lot if you click the 💚 and share with friends.*(可删?) --- -> [掘金翻译计划](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)、[React](https://github.com/xitu/gold-miner#react)、[前端](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)。 \ No newline at end of file +> [掘金翻译计划](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)、[React](https://github.com/xitu/gold-miner#react)、[前端](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)。 From 9df8dac2357ba3070b08880809c050cd61dad0f4 Mon Sep 17 00:00:00 2001 From: Wenlin Ou Date: Fri, 7 Apr 2017 22:25:45 -0400 Subject: [PATCH 076/638] typo fix --- ...ing-will-change-until-you-start-building.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/TODO/nothing-will-change-until-you-start-building.md b/TODO/nothing-will-change-until-you-start-building.md index 7d9880eb6ba..111ebd58e1e 100644 --- a/TODO/nothing-will-change-until-you-start-building.md +++ b/TODO/nothing-will-change-until-you-start-building.md @@ -10,15 +10,15 @@ # 真正行动之前 你将一无所成 -就在上周我打了一辆 Lyft 出租车,司机跟我聊了很多关于她的天花乱坠的想法。她的想法里,有一个关于儿童的书籍,有帮助人们停车的APP、有一个更高效的打包礼物的方法,但问题是她总是犹豫不决:她又想法但却不知道从何开始。 +就在上周我打了一辆 Lyft 出租车,司机跟我聊了很多关于她的天花乱坠的想法。她的想法里,有一个关于儿童书籍,有帮助人们停车的APP、有一个更高效的打包礼物的方法,但问题是她总是犹豫不决:她很有想法但却不知道从何开始。 -接下来这段话是我对她说的:**开始一些实际行动吧。选一个项目然后尽可能的去完成它。** 如果你想写一本书,那么从每天写一页开始;如果你像做一个APP,那就从草稿开始。任何人都可以做到这些。 +接下来这段话是我对她说的:**开始一些实际行动吧。选一个项目然后尽可能的去完成它。** 如果你想写一本书,那么从每天写一页开始;如果你想做一个APP,那就从草稿开始。任何人都可以做到这些。 这些建议对所有创新者都适用。一旦你开始着手在你的项目上,你将会不由自主的继续做下去。创新将会成为你人格的一部分 —— 即使你的项目失败了。 -你今天迈出的一小步将会是你人生的一大步。看看那些伟大的产品创造者吧,比如 [Drew Wilson](https://twitter.com/drewwilson), [Pieter Levels](https://twitter.com/levelsio?), 和 [Sebastian Dobrincu](https://twitter.com/Sebyddd)。**他们不仅仅是等待合适的时机。** 他们每周都在更新他们的产品。 +你今天迈出的一小步将会是你人生的一大步。看看那些伟大的产品缔造者吧,比如 [Drew Wilson](https://twitter.com/drewwilson), [Pieter Levels](https://twitter.com/levelsio?), 和 [Sebastian Dobrincu](https://twitter.com/Sebyddd)。**他们不仅仅是等待合适的时机。** 他们每周都在更新他们的产品。 -在一个 [IndieHackers](https://www.indiehackers.com/podcast/006-josh-pigford-of-baremetrics) 的节目中, [Josh Pigford](https://twitter.com/Shpigford) 透露,在做 Baremetrics 之前,他已经尝试过 *一百个* 失败的点子。 现在, Baremetrics 每个月都会有 60,000 美金进账。 +在一个 [IndieHackers](https://www.indiehackers.com/podcast/006-josh-pigford-of-baremetrics) 的节目中, [Josh Pigford](https://twitter.com/Shpigford) 透露,在做 Baremetrics 之前,他已经尝试过 *一百个* 失败的点子。 然而现在, Baremetrics 每个月都会有 60,000 美金进账。 ![](https://cdn-images-1.medium.com/max/800/1*BzmVaqAKEzNRybUmMYOxdA.png) @@ -31,11 +31,11 @@ 在开始的时候,确定你的项目要实现的主要目标。如果你想打破你以往的设计风格,那就把心思花在设计上;如果你测试一个新的前端框架,那就把钻研代码;如果你想获得上百个用户(”大量用户“ 会不会好点),就把精力用在销售上。 -**尽早确定目标可以帮助你摆脱诱惑,防止你到最后忘记初心。** 也许你的项目并不能成为下一个 Snapchat, 但是在通往你目标的道路上,你会更加的熟练运用各种做产品的工具。渐渐地,你会拥有越来越强大的设计、代码、销售等的能力。 +**尽早确定目标可以帮助你摆脱诱惑,防止你到最后忘记初心。** 也许你的项目并不能成为下一个 Snapchat, 但是在通往你目标的道路上,你会更加的熟练运用各种做产品的工具。渐渐地,你会拥有越来越强大的设计、代码、销售等能力。 ### 2. 执行计划 -有一些产品没能完的原因是作者在半路失去了动力。 **设定一些小目标(比如先赚一个亿);完成这些目标会让你获得前进的动力。** 小的成就累计起来也是很了不起的。 +有一些产品没能完成的原因是作者在半路失去了动力。 **设定一些小目标(比如先赚一个亿);完成这些目标会让你获得前进的动力。** 小的成就累计起来也是很了不起的。 ![](https://cdn-images-1.medium.com/max/800/1*ESildSVTSSOnFXGDxD-l9w.png) @@ -47,19 +47,19 @@ 解决一个问题。如果你自己也经历了这个问题那就再好不过了。问问你的朋友们他们都有哪些问题需要解决。 -比如,我做了 [YC Careers](http://jonathanzwhite.github.io/yc-careers/) 和 [AtomSpace](https://atomspace.co/) 来解决一个产品设计师朋友找工作和面试的烦恼。一夜之间,YC Careers 一下子爬到了 [ProductHunt](https://www.producthunt.com/posts/yc-careers) 的榜首,AtomSpace 在上线6个小时就拿到了来自陌生人的一百美元订单。 +比如,我做了 [YC Careers](http://jonathanzwhite.github.io/yc-careers/) 和 [AtomSpace](https://atomspace.co/) 来解决一个产品设计师朋友找工作和面试的烦恼。一夜之间,YC Careers 占领了 [ProductHunt](https://www.producthunt.com/posts/yc-careers) 的榜首,AtomSpace 在上线6个小时之内就拿到了来自陌生人的一百美元订单。 **你解决越大的痛点,就越容易找到用户。** 同时,尝试从过个角度分析问题和解决方案。有些成功的点子看起来并没有解决任何问题,但其实不然。比如,Instagram 看起来没有直接解决任何问题。其实不是。Instagram 满足了朋友之间实时社交的需求。如果这个需求不能得到满足,那才是个问题。 -想要了解更多的话,[John Carmack on Idea Generation](https://amasad.me/carmack) 和 [How to Get Startup Ideas](http://paulgraham.com/startupideas.html) 这两篇文章是非常好的开始。 +想要了解更多关于这方面内容的话,[John Carmack on Idea Generation](https://amasad.me/carmack) 和 [How to Get Startup Ideas](http://paulgraham.com/startupideas.html) 这两篇文章是非常好的起点。 ### 4. 放弃”好主意/坏主意“的想法 当你明确了你想要解决的问题并有了一个解决方案的时候,你可能会问自己这是不是一个好的点子。 -**相当一部分看起来很糟糕的点子最后被证明是超级棒的生意。** 比如,多年没人想投资 Airbnb。 Brian Chesky, Airbnb 的创始人之一,在他的文章 [7 Rejections](https://medium.com/@bchesky/7-rejections-7d894cbaa084#.l8fdqlasz) 里详细讲述了被投资人拒绝的故事。 +**相当一部分看起来很糟糕的点子最后被证明是超级棒的生意。** 比如,当年没人想投资 Airbnb。 Brian Chesky, Airbnb 的创始人之一,在他的文章 [7 Rejections](https://medium.com/@bchesky/7-rejections-7d894cbaa084#.l8fdqlasz) 里详细讲述了被投资人拒绝的故事。 ![](https://cdn-images-1.medium.com/max/800/1*WpxUxMCO-7NXr-o1yo023g.png) From 40c655106da805551994c8c586db94b6267adb63 Mon Sep 17 00:00:00 2001 From: sqrtthree Date: Sat, 8 Apr 2017 11:33:32 +0800 Subject: [PATCH 077/638] fix typos --- TODO/preload-prefetch-and-priorities-in-chrome.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO/preload-prefetch-and-priorities-in-chrome.md b/TODO/preload-prefetch-and-priorities-in-chrome.md index 150d1401a6a..5f13ea73d2b 100644 --- a/TODO/preload-prefetch-and-priorities-in-chrome.md +++ b/TODO/preload-prefetch-and-priorities-in-chrome.md @@ -107,7 +107,7 @@ preload 使用 “as” 属性加载的资源将会获得与资源 “type” ### 这将会浪费用户的带宽吗? -**用“preload”和“prefetch”情况下,如果资源不能被缓存,那么都有可能浪费一部分带宽。 +**用“preload”和“prefetch”情况下,如果资源不能被缓存,那么都有可能浪费一部分带宽。** 没有用到的 preload 资源在 Chrome 的 console 里会在 *onload* 事件 3s 后发生警告。 From d9add60f9fca9a897e37485caead9529c889380c Mon Sep 17 00:00:00 2001 From: ZhangFe Date: Sat, 8 Apr 2017 15:46:41 +0800 Subject: [PATCH 078/638] fix ** --- TODO/css-is-fine-its-just-really-hard.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/TODO/css-is-fine-its-just-really-hard.md b/TODO/css-is-fine-its-just-really-hard.md index 692e3cca856..3160c7e1bab 100644 --- a/TODO/css-is-fine-its-just-really-hard.md +++ b/TODO/css-is-fine-its-just-really-hard.md @@ -18,7 +18,7 @@ CSS 很棒。它只是太难了,所以我对它进行了一些编译。 ![](https://cdn-images-1.medium.com/max/1600/1*ioYNZ-FgsSpoos6b3iKblg.png) -很长一段时间以来, 我在电脑和手机上画了很多很多的矩形。我写了很多糟糕的 CSS,成千上万行差劲的 Less 代码和大量可怕的 Sass。他们已经融入我的血液了。 +很长一段时间以来,我在电脑和手机上画了很多很多的矩形。我写了很多糟糕的 CSS,成千上万行差劲的 Less 代码和大量可怕的 Sass。他们已经融入我的血液了。 但是我也写过很多很棒的 CSS!我使用 borders 画过三角形,使用 CSS transforms 绘制贝塞尔曲线,制作 60fps 的滚动动画,以及会让你大吃一惊的工具提示。 @@ -30,12 +30,12 @@ CSS性能极高。长久以来,很多人都在为了让 CSS 更快速,可调 — -当我年轻的时候,我发现每当我想让边框和超链接文本是同一个颜色的时候我都需要在两三个不同的地方去修改,这真是太可怕了。然后我发现了 LESS。现在我可以定义一个 `@wonderfulBlue` 并且在任何地方去使用它。*喂,Jordan,现在的 CSS 也有变量了...* +当我年轻的时候,我发现每当我想让边框和超链接文本是同一个颜色的时候我都需要在两三个不同的地方去修改,这真是太可怕了。然后我发现了 LESS。现在我可以定义一个 `@wonderfulBlue` 并且在任何地方去使用它。**喂,Jordan,现在的 CSS 也有变量了...** -然后我开始考虑为什么要为解释 `#left-section` 的宽度是546px(250 * 2 + 23 * 2)留下很长的注释?我开始使用 Less 写我的数学表达式:`2 * @sectionWidth + 2 * @sectionPadding`。*我猜测你不熟悉 calc(),因为它浏览器兼容性不好* +然后我开始考虑为什么要为解释 `#left-section` 的宽度是546px(250 * 2 + 23 * 2)留下很长的注释?我开始使用 Less 写我的数学表达式:`2 * @sectionWidth + 2 * @sectionPadding`。**我猜测你不熟悉 calc(),因为它浏览器兼容性不好** -当年 `border-radius` 需要被 polyfill 时,我在所有使用到的地方添加前缀。后来我使用了 `border-radius()` mixin,这样只要在我需要使用的时候把代码添加上就可以了。*好吧如果你只用到了组件分类呢 —*。伙计你能停一停么?让我完成我的文章先。*我错了—*。没事,别担心,继续听下去。 +当年 `border-radius` 需要被 polyfill 时,我在所有使用到的地方添加前缀。后来我使用了 `border-radius()` mixin,这样只要在我需要使用的时候把代码添加上就可以了。**好吧如果你只用到了组件分类呢 — **。伙计你能停一停么?让我完成我的文章先。**我错了 — **。没事,别担心,继续听下去。 当 CSS 解决不了我的问题时,我开始写 Less。它们会被编译成 CSS,并且它在我的用户的设备上工作的非常棒。只是我比原来忙了 10 倍,我无法单独去编写它了。 @@ -45,7 +45,7 @@ CSS性能极高。长久以来,很多人都在为了让 CSS 更快速,可调 他们中的某些页面已经很庞大了,因此通常我们会将我们的 CSS (好吧,Less)和 JavaScript 分割成独立的文件,这样用户就不必下载练习页面的代码来观看视频。 -有些时候,我们移除了很多代码后样式看起来就不对了。因为我们的主页菜单可能希望有一个 `.left-arrow` 类,但是现在这个 class 的样式在 `exercise.css` 文件里。通常我们注意不到这点,因为导航条被鼠标点击几次后 `.left-arrow` 会被整齐地卷起来。*这么看来你应该有截图测试或更严格的 QA 过程*,我刚才说了什么来着? +有些时候,我们移除了很多代码后样式看起来就不对了。因为我们的主页菜单可能希望有一个 `.left-arrow` 类,但是现在这个 class 的样式在 `exercise.css` 文件里。通常我们注意不到这点,因为导航条被鼠标点击几次后 `.left-arrow` 会被整齐地卷起来。**这么看来你应该有截图测试或更严格的 QA 过程**,我刚才说了什么来着? 唉,这是很辛苦的工作!但是代码就是偶尔会出 bug,修复它们并且继续前进,这是件很酷的事。 @@ -68,7 +68,7 @@ CSS性能极高。长久以来,很多人都在为了让 CSS 更快速,可调 CSS很好,速度很快,它已经发展了有20多年了,并且适用于各种应用程序。 -但是我真的不喜欢写 CSS。很多人也不喜欢,所以我们开发了这些很棒的模式去写 CSS。但是我也不喜欢以这些模式去写,我有更好的事情要去做。并且 JavaScript 也很酷。*实际上 JavaScript 有更多的可能性*。[所以我用 JavaScript 去编写我的 CSS](https://github.com/khan/aphrodite). +但是我真的不喜欢写 CSS。很多人也不喜欢,所以我们开发了这些很棒的模式去写 CSS。但是我也不喜欢以这些模式去写,我有更好的事情要去做。并且 JavaScript 也很酷。**实际上 JavaScript 有更多的可能性**。[所以我用 JavaScript 去编写我的 CSS](https://github.com/khan/aphrodite). 把这样的代码: From 01ef70394f6f662541c7f6f9eac0996902a9b094 Mon Sep 17 00:00:00 2001 From: ZhangFe Date: Sat, 8 Apr 2017 15:53:25 +0800 Subject: [PATCH 079/638] =?UTF-8?q?fix=E8=BD=AC=E6=8D=A2=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO/css-is-fine-its-just-really-hard.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO/css-is-fine-its-just-really-hard.md b/TODO/css-is-fine-its-just-really-hard.md index 3160c7e1bab..423250fcfb8 100644 --- a/TODO/css-is-fine-its-just-really-hard.md +++ b/TODO/css-is-fine-its-just-really-hard.md @@ -35,7 +35,7 @@ CSS性能极高。长久以来,很多人都在为了让 CSS 更快速,可调 然后我开始考虑为什么要为解释 `#left-section` 的宽度是546px(250 * 2 + 23 * 2)留下很长的注释?我开始使用 Less 写我的数学表达式:`2 * @sectionWidth + 2 * @sectionPadding`。**我猜测你不熟悉 calc(),因为它浏览器兼容性不好** -当年 `border-radius` 需要被 polyfill 时,我在所有使用到的地方添加前缀。后来我使用了 `border-radius()` mixin,这样只要在我需要使用的时候把代码添加上就可以了。**好吧如果你只用到了组件分类呢 — **。伙计你能停一停么?让我完成我的文章先。**我错了 — **。没事,别担心,继续听下去。 +当年 `border-radius` 需要被 polyfill 时,我在所有使用到的地方添加前缀。后来我使用了 `border-radius()` mixin,这样只要在我需要使用的时候把代码添加上就可以了。**好吧如果你只用到了组件分类呢 —**。伙计你能停一停么?让我完成我的文章先。**我错了 —**。没事,别担心,继续听下去。 当 CSS 解决不了我的问题时,我开始写 Less。它们会被编译成 CSS,并且它在我的用户的设备上工作的非常棒。只是我比原来忙了 10 倍,我无法单独去编写它了。 From 9886d460d18b7e1f4d32fd86d87814861034d315 Mon Sep 17 00:00:00 2001 From: reid Date: Sat, 8 Apr 2017 17:26:48 +0800 Subject: [PATCH 080/638] =?UTF-8?q?=E4=BA=8C=E6=A0=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...k-bits-getting-the-most-out-of-the-commonschunkplugin.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/TODO/webpack-bits-getting-the-most-out-of-the-commonschunkplugin.md b/TODO/webpack-bits-getting-the-most-out-of-the-commonschunkplugin.md index 1d88f590d61..cd003ab1e9f 100644 --- a/TODO/webpack-bits-getting-the-most-out-of-the-commonschunkplugin.md +++ b/TODO/webpack-bits-getting-the-most-out-of-the-commonschunkplugin.md @@ -91,7 +91,7 @@ webpack 核心团队隔三差五地就会在 Twitter 上作一些寓教于乐的 filename: "commonlazy.js" }); -类似地 —— webpack 会扫描所有 chunks 并检查公共模块。由于设置了 `async: true`,只有代码拆分的 bundles 会被扫描。因为我们并没有指明 `minChunks` 的值,所有 webpack 会取其默认值 3。综上,上述代码的含义是: +类似地 —— webpack 会扫描所有 chunks 并检查公共模块。由于设置了 `async: true`,只有代码拆分的 bundles 会被扫描。因为我们并没有指明 `minChunks` 的值,所以 webpack 会取其默认值 3。综上,上述代码的含义是: > **嘿 webpack,请检查所有的普通(即懒加载的)chunks,如果某个模块出现在了 3 个或 3 个以上的 chunks 中,就将其分离到一个独立的异步公共 chunk 中去。** @@ -99,7 +99,7 @@ webpack 核心团队隔三差五地就会在 Twitter 上作一些寓教于乐的 ![Markdown](http://i4.buimg.com/1949/626cbab70072f442.png) -现在异步 chunks 都非常的小,并且所有代码都被聚合到 `commonlazy.js` 文件中去了。因为这些 bundles 本来就很小了, 首次访问可能都察觉不到代码体积的变化。现在,每一个代码拆分的 bundle 所需携带的数据更少了;而且,通过将这些公共模块放到一个独立可缓存的 chunk 中,我们节省了用户加载时间,降低了数据消费量(data consumption)。 +现在异步 chunks 都非常的小,并且所有代码都被聚合到 `commonlazy.js` 文件中去了。因为这些 bundles 本来就很小了, 首次访问可能都察觉不到代码体积的变化。现在,每一个代码拆分的 bundle 所需携带的数据更少了;而且,通过将这些公共模块放到一个独立可缓存的 chunk 中,我们节省了用户加载时间,减少了需要传输的数据量(data consumption)。 #### 更多控制:minChunks 函数 #### @@ -129,7 +129,7 @@ webpack 核心团队隔三差五地就会在 Twitter 上作一些寓教于乐的 function lodashMomentModuleFilter(module, count) { return module.resource && /lodash|moment/.test(module.resource) && count >= 2; } - + function immutableReactModuleFilter(module, count) { return module.resource && /immutable|react/.test(module.resource) && count >=4 } From 719e846484632c4e1d2ca7b654644ee4d1987a76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=94=85=E7=82=89=E5=B7=A5?= Date: Sat, 8 Apr 2017 18:14:32 +0800 Subject: [PATCH 081/638] modified according to @Vivienmm's revision --- ...-java-2-java-8-stream-android-rxjava2-hell-part4.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/TODO/war-learning-curve-rx-java-2-java-8-stream-android-rxjava2-hell-part4.md b/TODO/war-learning-curve-rx-java-2-java-8-stream-android-rxjava2-hell-part4.md index bdc42075a07..b0b63eeb7c5 100644 --- a/TODO/war-learning-curve-rx-java-2-java-8-stream-android-rxjava2-hell-part4.md +++ b/TODO/war-learning-curve-rx-java-2-java-8-stream-android-rxjava2-hell-part4.md @@ -11,13 +11,13 @@ 又是新的一天,如果学点新东西,这一天一定会很酷炫。 -小伙伴们一切顺利啊,这是我们的 RxJava2 Android 系列的第四部分 [ [第一部分](https://github.com/xitu/gold-miner/blob/master/TODO/pull-vs-push-imperative-vs-reactive-reactive-programming-android-rxjava2-hell-part2.md), [第二部分](http://www.uwanttolearn.com/android/pull-vs-push-imperative-vs-reactive-reactive-programming-android-rxjava2-hell-part2/), [第三部分](https://github.com/xitu/gold-miner/blob/master/TODO/functional-interfaces-functional-programming-and-lambda-expressions-reactive-programming-android-rxjava2-what-the-hell-is-this-part3.md) ]。 好消息是我们已经满足了一些先决条件,已经准备好用 Rx 来工作了。在使用 RxJava2 Android Observable 之前,我会先用 Java8 的 Stream 来做响应式编程。我认为我们应该了解 Java8,我还感觉通过使用 Java8 的流式 API 会让 RxJava2 Android 的学习曲线更简单。 +小伙伴们一切顺利啊,这是我们的 RxJava2 Android 系列的第四部分 [ [第一部分](https://github.com/xitu/gold-miner/blob/master/TODO/pull-vs-push-imperative-vs-reactive-reactive-programming-android-rxjava2-hell-part2.md), [第二部分](http://www.uwanttolearn.com/android/pull-vs-push-imperative-vs-reactive-reactive-programming-android-rxjava2-hell-part2/), [第三部分](https://github.com/xitu/gold-miner/blob/master/TODO/functional-interfaces-functional-programming-and-lambda-expressions-reactive-programming-android-rxjava2-what-the-hell-is-this-part3.md) ]。 好消息是我们已经做好准备,可以开始使用 Rx 了。在使用 RxJava2 Android Observable 之前,我会先用 Java8 的 Stream 来做响应式编程。我认为我们应该了解 Java8,我还感觉通过使用 Java8 的流式 API 会让 RxJava2 Android 的学习曲线更简单。 **动机:** 动机跟我在 [第一部分](https://github.com/xitu/gold-miner/blob/master/TODO/pull-vs-push-imperative-vs-reactive-reactive-programming-android-rxjava2-hell-part2.md) 和大家分享过的一样。在我开始学习 RxJava2 Android 的时候,我并不知道我该怎样使用、在什么地方使用它。 -现在我们知道学习这个需要一些先决条件,但当初我对此一无所知。在我刚刚开始的时候,该如何从任何数据或对象上创建一个 Observable 呢。然后我知道当 Observable 上的数据发生了一些事件的时候,有接口或者可以叫做“回调”会被调用。这在理论上很棒,但是当我付诸实践的时候,GG了。我发现很多理论上应该成立的模式在我去用的时候完全不起作用。对我来说最大的问题,是不能用响应或者函数式响应的思维思考问题。我有命令式编程和面向对象编程的背景,由于先入为主,所以对我来说理解会有些难。我一直在问这些问题:我该在哪里实现?我应该怎么实现?如果你跟着这篇文章看下来,我可以 100% 保证你会知道怎样把命令式代码转换到 Rx 代码,虽然写出来的 Rx 代码可能不是很好,但是你起码知道怎么开始。 +现在我们已经学会了一些预备知识,但当时我什么都不知道。我那时从学习如何从数据或对象创建 Observable 开始。然后学会当 Observable 数据上发生变化时,去调用哪些接口(或者可以叫做“回调”)。这在理论上很棒,但是当我付诸实践的时候,GG了。我发现很多理论上应该成立的模式在我去用的时候完全不起作用。对我来说最大的问题,是不能用响应或者函数式响应的思维思考问题。我有命令式编程和面向对象编程的背景,由于先入为主,所以对我来说理解会有些难。我一直在问这些问题:我该在哪里实现?我应该怎么实现?如果你跟着这篇文章看下来,我可以 100% 保证你会知道怎样把命令式代码转换到 Rx 代码,虽然写出来的 Rx 代码可能不是很好,但是你起码知道怎么开始。 **回顾:** @@ -50,7 +50,7 @@ Stream: - 用来支持在元素形成的流上进行函数式操作(比如在集合上进行的 map-reduce 变换)的类(*docs.oracle*)。 +支持在元素形成的流上进行函数式操作(比如在集合上进行的 map-reduce 变换)的类(*docs.oracle*)。 第一个问题:在英语中 Stream 是什么意思? @@ -156,7 +156,7 @@ public static void main(String[] args) { 同志们这里需要注意下! -在 Java8 Stream 中有个叫做 Predicate(谓词,可以判断真假,详情见线性代数中的相关定义——译注)的函数式接口。所以,如果我想进行过滤的话需要把这个函数式接口送到流的过滤器函数里面。现在,我给大家展示在我们的代码中如何创建“大石子过滤器”。 +在 Java8 Stream 中有个叫做 Predicate(谓词,可以判断真假,详情见离散数学中的相关定义——译注)的函数式接口。所以,如果我想进行过滤的话,可以用这个函数式接口实现流的过滤功能。现在,我给大家展示在我们的代码中如何创建“大石子过滤器”。 ``` private static Predicate BigStoneFilter = new Predicate() { @@ -767,7 +767,7 @@ Observable.range(0, 10) **总结:** -同志们干得好!今天我们学 Rx Android 学得很开心。我们从图画开始,然后使用了 Java8 的流(Stream)。之后将 Java8 的流转换到 RxJava 2 Android 的 Observable。再之后,我们看到了真实项目中的示例并且展示了在现有的项目中如何开始使用 Rx,把 if 换成 filter,用 map 进行数据转化,用 flatmap 代替嵌套的循环。下篇文章: [Dialogue between Rx Observable and a Developer (Me) [ Android RxJava2 ] ( What the hell is this ) Part5](http://www.uwanttolearn.com/android/dialogue-rx-observable-developer-android-rxjava2-hell-part5/). +同志们干得好!今天我们学 Rx Android 学得很开心。我们从图画开始,然后使用了 Java8 的流(Stream)。之后将 Java8 的流转换到 RxJava 2 Android 的 Observable。再之后,我们看到了真实项目中的示例并且展示了在现有的项目中如何开始使用 Rx。最后,我展示了一些转换到 Rx 的技巧:把循环用 forEach 替换,把 if 换成 filter,用 map 进行数据转化,用 flatmap 代替嵌套的循环。下篇文章: [Dialogue between Rx Observable and a Developer (Me) [ Android RxJava2 ] ( What the hell is this ) Part5](http://www.uwanttolearn.com/android/dialogue-rx-observable-developer-android-rxjava2-hell-part5/). 希望你们开心,同志们再见! From a57c45737bc90135aac4b4343880fc81159f1b0e Mon Sep 17 00:00:00 2001 From: zhuzi Date: Sat, 8 Apr 2017 20:12:00 +0800 Subject: [PATCH 082/638] add proofreader info --- TODO/using-devtools-tweak-designs-browser.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO/using-devtools-tweak-designs-browser.md b/TODO/using-devtools-tweak-designs-browser.md index fe280545985..9fc7266025c 100644 --- a/TODO/using-devtools-tweak-designs-browser.md +++ b/TODO/using-devtools-tweak-designs-browser.md @@ -2,7 +2,7 @@ > * 原文作者:[AHMAD SHADEED](https://css-tricks.com/author/shadeed9/) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 译者:[bambooom](https://github.com/bambooom) -> * 校对者: +> * 校对者:[gy134340](https://github.com/gy134340) / [avocadowang](https://github.com/avocadowang) # 使用开发者工具在浏览器中调整设计 From 411b25901dc1d966ea0c32cfecfc26b40d05c9eb Mon Sep 17 00:00:00 2001 From: gy134340 Date: Sat, 8 Apr 2017 22:57:16 +0800 Subject: [PATCH 083/638] =?UTF-8?q?=E8=B7=8C=E5=AE=95=E8=B5=B7=E4=BC=8F?= =?UTF-8?q?=E7=9A=84=E5=87=BD=E6=95=B0=E5=BC=8F=E7=BC=96=E7=A8=8B=EF=BC=88?= =?UTF-8?q?=E7=BB=84=E6=88=90=E5=8C=96=E8=BD=AF=E4=BB=B6=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ctional-programming-composable-software.md | 124 +++++++++--------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/TODO/the-rise-and-fall-and-rise-of-functional-programming-composable-software.md b/TODO/the-rise-and-fall-and-rise-of-functional-programming-composable-software.md index fda67ce9863..463b3cbdc66 100644 --- a/TODO/the-rise-and-fall-and-rise-of-functional-programming-composable-software.md +++ b/TODO/the-rise-and-fall-and-rise-of-functional-programming-composable-software.md @@ -4,117 +4,117 @@ > * 译者: > * 校对者: -# The Rise and Fall and Rise of Functional Programming (Composing Software) +# 跌宕起伏的函数式编程(组成化软件) -Smoke Art Cubes to Smoke — MattysFlicks — (CC BY 2.0) +烟雾的方块艺术 —MattysFlicks —(CC BY 2.0) -> Note: This is part 1 of the “Composing Software” series on learning functional programming and compositional software techniques in JavaScript ES6+ from the ground up. Stay tuned. There’s a lot more of this to come! +> 注意:这是从基础学习函数式编程和使用 JavaScript ES6+ 组成软件的第一部分。保持关注,接下来还有很多! -When I was about 6 years old, I spent a lot of time playing computer games with my best friend. His family had a room full of computers. To me, they were irresistible. Magic. I spent many hours exploring all the games. One day I asked my friend, “how do we make a game?” +当我 6 岁时,我花了很多时间跟我的小伙伴玩电脑游戏,他家里有一整房间的电脑。对于我说,那是及其不可抗拒的魔力。我花了很多时间探索所有的游戏。一天我问他,“我们怎样做一个游戏?” -He didn’t know, so we asked his dad, who reached up on a high shelf and pulled down a book of games written in Basic. So began my journey with programming. By the time public school got around to teaching algebra, I already knew the topic well, because programming is basically algebra. It can be, anyway. +他不知道,所以我们问了他的老爸,他的老爸爬上一个很高的架子拿下来一本使用 Basic 编写的游戏书籍。就那样开始了我的编程之路。当公立学校开始教授代数时,我已经熟稔其中的概念了,因为编程基本上是代数。无论如何,它都是。 -### The Rise of Composable Software +### 软件组成化的兴起 -In the beginning of computer science, before most of computer science was actually done on computers, there lived two great computer scientists: Alonzo Church, and Alan Turing. They produced two different, but equivalent universal models of computation. Both models could compute anything that can be computed (hence, “universal”). +在计算机科学的起步阶段,在大多数的计算机科学在电脑上完成之前,有两位伟大的计算机科学家:阿隆佐·邱奇和艾伦·图灵。他们发明了两种不同、但是普遍通用的计算模型。两种都可以计算所有可被计算的东西(因此,“普遍”)。 -Alonzo Church invented lambda calculus. Lambda calculus is a universal model of computation based on function application. Alan Turing is known for the turing machine. A turing machine is a universal model of computation that defines a theoretical device that manipulates symbols on a strip of tape. +阿隆佐·邱奇发明了 lambda 表达式。lambda 表达式是基于函数应用的通用计算模型。艾伦·图灵因为图灵机而知名。图灵机使用定义一个在磁带上操作符号的理论装置来计算的通用模型。 -Together, they collaborated to show that lambda calculus and the turing machine are functionally equivalent. +总的说,他们共同说明了 lambda 表达式和图灵机功能上是相等的。 -Lambda calculus is all about function composition. Thinking in terms of function composition is a remarkably expressive and eloquent way to compose software. In this text, we’re going to discuss the importance of function composition in software design. +lambda 表达式全是函数组成,依靠函数来编写软件是非常高效和有意义的。本文中,我们将会讨论软件设计中函数的组成的重要性。 -There are three important points that make lambda calculus special: +有三点造就了 lambda 表达式的特别之处: -1. Functions are always anonymous. In JavaScript, the right side of `const sum = (x, y) => x + y` is the *anonymous* function expression `(x, y) => x + y`. -2. Functions in lambda calculus only accept a single input. They’re unary. If you need more than one parameter, the function will take one input and return a new function that takes the next, and so on. The n-ary function `(x, y) => x + y` can be expressed as a unary function like: `x => y => x + y`. This transformation from an n-ary function to a unary function is known as currying. -3. Functions are first-class, meaning that functions can be used as inputs to other functions, and functions can return functions. +1. 函数都是匿名的,在 JavaScript 中,右边的 `const sum = (x, y) => x + y` 等同于匿名函数 `(x, y) => x + y`. +2. lambda 表达式中的函数只接收一个参数。他们是一元的,如果你需要多个参数,函数将会接受一个输入返回一个调用下一个函数的函数,然后继续这样。非一元函数 `(x, y) => x + y` 可以被表示为一个像 `x => y => x + y` 的一元函数。这个把多元函数转换成一元函数的过程叫做柯里化。 +3. 函数是一级的,意味着函数可以作为参数传递给其他函数,同时函数可以返回函数。 -Together, these features form a simple, yet expressive vocabulary for composing software using functions as the primary building block. In JavaScript, anonymous functions and curried functions are optional features. While JavaScript supports important features of lambda calculus, it does not enforce them. +总的说来,这些特性形成使用函数作为初始模块的一个简单且具有表达性的方法来构造软件。在 JavaScript 中,函数的匿名和柯里化都是可选的特性。虽然 JavaScript 支持这些 lambda 表达式的重要属性,它却并不强制使用这些。 -The classic function composition takes the output from one function and uses it as the input for another function. For example, the composition: +这些经典的函数组成方法用一个函数的输出来作为另一个函数的输入,例如,对于组合: - f . g + f . g + +也可以写做: -Can be written as: + compose2 = f => g => x => f(g(x)) + +这里是你使用它的方法: + + double = n => n * 2 + inc = n => n + 1 + + compose2(double)(inc)(3) - compose2 = f => g => x => f(g(x)) +`compose2()` 函数使用 `double` 函数作为第一个参数,使用 `inc` 函数作为第二个参数,同时对于两个函数的组合传入参数 `3`。再看一下 `compose2()` 函数所写的,`f` 是 `double()`,`g` 是 `inc()`,同时 `x` 是 `3`。函数 `compose2(double)(inc)(3)` 的调用,实际上是三个不同函数的调用: -Here’s how you’d use it: +1. 首先传入 `double` 同时返回一个新的函数。 +2. 返回的函数传入 `inc` 同时再返回一个新的函数。 +3. 再返回的函数传入 `3` 同时计算 `f(g(x))`,最后实际上是 `double(inc(3))`。 +4. `x` 等于 `3` 同时传给 `inc()`。 +5. `inc(3)` 等于 `4`。 +6. `double(4)` 等于 `8`。 +7. 函数返回 `8`。 - double = n => n * 2 - inc = n => n + 1 +组成软件时,可以被看作一个由函数组成的图。看一下下面: + + append = s1 => s2 => s1 + s2 + append('Hello, ')('world!') - compose2(double)(inc)(3) - -The `compose2()` function takes the `double` function as the first argument, the `inc` function as the second, and then applies the composition of those two functions to the argument `3`. Looking at the signature of `compose2()` again, `f` is `double()`, `g` is `inc()`, and `x` is `3`. The function call, `compose2(double)(inc)(3)`, is actually 3 different function invocations: - -1. The first passes `double` and returns a new function. -2. The returned function takes `inc` and returns a new function. -3. The next returned function takes `3` and evaluates `f(g(x))`, which is now `double(inc(3))`. -4. `x` evaluates to `3` and gets passed into `inc()`. -5. `inc(3)` evaluates to `4`. -6. `double(4)` evaluates to `8`. -7. `8` gets returned from the function. - -When software is composed, it can be represented by a graph of function compositions. Consider the following: - - append = s1 => s2 => s1 + s2 - append('Hello, ')('world!') - -You could represent it visually: +你可以想象成这样: -Lambda calculus was hugely influential on software design, and prior to about 1980, many very influential icons of computer science were building software using function composition. Lisp was created in 1958, and was heavily influenced by lambda calculus. Today, Lisp is the second-oldest language that’s still in popular use. +lambda 表达式对软件设计产生了很大的影响,在 1980 年之前,计算机科学领域很多有影响的东西使用函数来构造软件。Lisp 在 1958 年被创作出来,很大程度上受到了 lambda 表达式的影响。如今,Lisp 是广泛使用的第二老的语言了。 -I was introduced to it through AutoLISP: the scripting language used in the most popular Computer Aided Design (CAD) software: AutoCAD. AutoCAD is so popular, virtually every other CAD application supports AutoLISP so that they can be compatible. Lisp is also a popular teaching language in computer science curriculum for three reasons: +我通过 AutoLISP:一个作为脚本语言被用于最流行的计算机辅助设计(CAD)软件:AutoCAD,接触到它。AutoCAD 很流行,实际上所有其他的 CAD 软件都兼容支持 AutoLISP。Lisp 因为以下三点原因被广泛作为计算机科学的课程: -1. Its simplicity makes it easy to learn the basic syntax and semantics of Lisp in about a day. -2. Lisp is all about function composition, and function composition is an elegant way to structure applications. -3. The best computer science text book I know of uses Lisp: [Structure and Interpretation of Computer Programs](https://www.amazon.com/Structure-Interpretation-Computer-Programs-Engineering/dp/0262510871/ref=as_li_ss_tl?ie=UTF8&linkCode=ll1&tag=eejs-20&linkId=4896ed63eee8657b6379c2acd99dd3f3). +1. 可以很容易的在一天左右学习 Lisp 基础的词法和语法。 +2. Lisp 全是由函数组成,函数组合是构造应用非常优雅的方式。 +3. 我知道的使用 Lisp 的最棒的计算机科学书籍:[计算机程序的结构与解释](https://www.amazon.com/Structure-Interpretation-Computer-Programs-Engineering/dp/0262510871/ref=as_li_ss_tl?ie=UTF8&linkCode=ll1&tag=eejs-20&linkId=4896ed63eee8657b6379c2acd99dd3f3)。 -### The Fall of Composable Software +### 组合型软件的衰落 -Somewhere between 1970 and 1980, the way that software was created drifted away from simple compositions, and became a list of linear instructions for the computer to follow. Then came object-oriented programming — a great idea about component encapsulation and message passing that got distorted by popular languages into a horrible idea about inheritance hierarchies and **is-a** relationships for feature reuse. +在 1970 到 1980 中间的某段时间,软件的构造开始偏离简单的组合,成为一串线性的让计算机执行的指令。然后面向对象编程 — 一个伟大的关于组件的封装和信息传递的思想被流行的编程语言扭曲成为了特性的重用而采取的糟糕的继承层次和 *is-a* 关系。 -Functional programming was relegated to the sidelines and academia: The blissful obsession of the geekiest of programming geeks, professors in their ivy league towers, and some lucky students who escaped the Java force-feeding obsession of the 1990’s — 2010's. +函数式编程语言退居二线:只有编程极客的痴迷、常春藤盟校的教授和一些幸运的学生可以在 1990 — 2010 年间逃离 Java 的强迫性学习。 -For most of us, creating software was a bit of a nightmare for 30 years. Dark times. +对于我们的大多数人来说,已经经历了大约 30 年的软件编写噩梦和黑暗时期。 -### The Rise of Composable Software +### 组合型软件的兴起 -Around 2010, something great began to happen: JavaScript exploded. Before about 2006, JavaScript was widely considered a toy language used to make cute animations happen in web browsers, but it had some powerful features hidden in it. Namely, the most important features of lambda calculus. People started whispering in the shadows about this cool new thing called “functional programming”. +在 2010 年左右,一些有趣的事情发生了:JavaScript 的崛起。在 2006 年左右,JavaScript 被广泛的看作玩具语言和被用制作浏览器中好玩的动画,但是它有一些隐藏饿及其强大的属性。即 lambda 表达式中最重要的特性。人们开始暗中讨论一个叫做 “函数式编程的” 酷东西。 [![](https://i.embed.ly/1/display/resize?url=https%3A%2F%2Fpbs.twimg.com%2Fprofile_images%2F3722564505%2Fb8030b8f3990875e8f38ed877fdf8d25_bigger.png&key=4fce0568f2ce49e8b54624ef71a8a5bd&width=40)](https://video.twimg.com/tweet_video/CmectVVVUAAsvpo.mp4) -I keep telling you [#JavaScript](https://twitter.com/hashtag/JavaScript?src=hash) is not a toy language. Now I have to show you. [#StarWars](https://twitter.com/hashtag/StarWars?src=hash) [@BrendanEich](https://twitter.com/BrendanEich) +我一直在告诉大家 [JavaScript] 并不是一门玩具语言。现在我需要展示它。[#StarWars](https://twitter.com/hashtag/StarWars?src=hash) [@BrendanEich](https://twitter.com/BrendanEich) -By 2015, the idea of building software with function composition was popular again. To make it simpler, the JavaScript specification got its first major upgrade of the decade and added arrow functions, which made it easier to create and read functions, currying, and lambda expressions. +在 2015 年,使用函数的组合来编写软件又开始流行起来。为了更简单化,JavaScript 规范获得的数十年来第一次主要的更新并且添加了箭头函数,为了更简单的编写和读取函数、柯里化,和 lambda 语句。 -Arrow functions were like rocket fuel for the functional programming explosion in JavaScript. Today it’s rare to see a large application which doesn’t use a lot of functional programming techniques. +箭头函数像是 JavaScript 函数式编程飞升的燃料。现在很少看见不使用很多函数式编程技术的大型应用了。 -Composition is a simple, elegant, and expressive way to clearly model the behavior of software. The process of composing small, deterministic functions to create larger software components and functionality produces software that is easier to organize, understand, debug, extend, test, and maintain. +组合型可以简单、优雅的表达软件的模型和行为。通过把小的、确定的函数组成稍大的组件并构成软件的过程,可以更为简单的组织、理解、调试、扩展、测试和掌控。 -As you read the following text, please experiment with the examples. Remember what it was like as a child to pick things apart, explore, and play while you learn. Rediscover the childhood joy of discovery. Let’s make some magic. +你在阅读下一部分时,可以使用实例实验,记住要把你自己当孩子一样把其他的思想扔在一边在学习中去探索和玩耍。重新发现孩童时发现新事物的欣喜。让我们来做一些魔术吧。 -[Continued in part 2: “Why Learn Functional Programming in JavaScript?”](https://medium.com/javascript-scene/why-learn-functional-programming-in-javascript-composing-software-ea13afc7a257#.i6vf0q8uy) +[接下来的第二部分:“为什么要用 JavaScript 学习函数式编程?”] -### Next Steps +### 下一步 -Want to learn more about functional programming in JavaScript? +想更多的学习 JavaScript 的函数式编程? -[Learn JavaScript with Eric Elliott](http://ericelliottjs.com/product/lifetime-access-pass/). If you’re not a member, you’re missing out! +[Learn JavaScript with Eric Elliott](http://ericelliottjs.com/product/lifetime-access-pass/),什么,你还不是其中之一,out 了! [![](https://cdn-images-1.medium.com/freeze/max/30/1*3njisYUeHOdyLCGZ8czt_w.jpeg?q=20)![](https://cdn-images-1.medium.com/max/800/1*3njisYUeHOdyLCGZ8czt_w.jpeg)](https://ericelliottjs.com/product/lifetime-access-pass/) -***Eric Elliott*** is the author of [*“Programming JavaScript Applications”*](http://pjabook.com) (O’Reilly), and [*“Learn JavaScript with Eric Elliott”*](http://ericelliottjs.com/product/lifetime-access-pass/). He has contributed to software experiences for **Adobe Systems, Zumba Fitness, he Wall Street Journal, ESPN, BBC, and top recording artists including Usher, Frank Ocean, Metallica**, and many more. +*Eric Elliott* 是 [*“Programming JavaScript Applications”*](http://pjabook.com) (O’Reilly) 和 “Learn JavaScript with Eric Elliott” 的作者。他曾效力于 *Adobe Systems, Zumba Fitness, he Wall Street Journal, ESPN, BBC, and top recording artists including Usher, Frank Ocean, Metallica* 和其他一些公司。 -*He spends most of his time in the San Francisco Bay Area with the most beautiful woman in the world.* +*他和她的老婆(很漂亮)大部分时间都在旧金山湾区里。* --- From 4479517a81c541ddd708327ce6e127ca1190575c Mon Sep 17 00:00:00 2001 From: Wenlin Ou Date: Sun, 9 Apr 2017 00:45:08 -0400 Subject: [PATCH 084/638] fix errors according proofreader --- ...ng-will-change-until-you-start-building.md | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/TODO/nothing-will-change-until-you-start-building.md b/TODO/nothing-will-change-until-you-start-building.md index 111ebd58e1e..034e8ae1484 100644 --- a/TODO/nothing-will-change-until-you-start-building.md +++ b/TODO/nothing-will-change-until-you-start-building.md @@ -10,11 +10,11 @@ # 真正行动之前 你将一无所成 -就在上周我打了一辆 Lyft 出租车,司机跟我聊了很多关于她的天花乱坠的想法。她的想法里,有一个关于儿童书籍,有帮助人们停车的APP、有一个更高效的打包礼物的方法,但问题是她总是犹豫不决:她很有想法但却不知道从何开始。 +就在上周我打了一辆 Lyft 出租车,司机跟我聊了很多关于她的天花乱坠的想法。她的想法里,她有写一本儿童书籍的想法,有开发一个帮助人们找到停车位置的软件的想法,有寻找到一种更高效打包礼物的想法,但问题是她总是犹豫不决:她有很多想法但却不知道从何开始。 -接下来这段话是我对她说的:**开始一些实际行动吧。选一个项目然后尽可能的去完成它。** 如果你想写一本书,那么从每天写一页开始;如果你想做一个APP,那就从草稿开始。任何人都可以做到这些。 +接下来这段话是我对她说的:**开始一些实际行动吧。选一个项目然后尽可能的去完成它。** 如果你想写一本书,那么从每天写一页开始;如果你想做一个APP,那就从构建草图开始。任何人都可以做到这些。 -这些建议对所有创新者都适用。一旦你开始着手在你的项目上,你将会不由自主的继续做下去。创新将会成为你人格的一部分 —— 即使你的项目失败了。 +这个建议适用于所有的创作者。一旦你开始着手在你的项目上,你将会不由自主的继续做下去。创新将会成为你人格的一部分 —— 即使你的项目失败了。 你今天迈出的一小步将会是你人生的一大步。看看那些伟大的产品缔造者吧,比如 [Drew Wilson](https://twitter.com/drewwilson), [Pieter Levels](https://twitter.com/levelsio?), 和 [Sebastian Dobrincu](https://twitter.com/Sebyddd)。**他们不仅仅是等待合适的时机。** 他们每周都在更新他们的产品。 @@ -29,11 +29,11 @@ ### 1. 确定目标 -在开始的时候,确定你的项目要实现的主要目标。如果你想打破你以往的设计风格,那就把心思花在设计上;如果你测试一个新的前端框架,那就把钻研代码;如果你想获得上百个用户(”大量用户“ 会不会好点),就把精力用在销售上。 +在开始的时候,确定你的项目要实现的主要目标。如果你想打破你以往的设计风格,那就把心思花在设计上;如果你测试一个新的前端框架,那就钻研代码;如果你想获得大量用户,就把精力用在销售上。 -**尽早确定目标可以帮助你摆脱诱惑,防止你到最后忘记初心。** 也许你的项目并不能成为下一个 Snapchat, 但是在通往你目标的道路上,你会更加的熟练运用各种做产品的工具。渐渐地,你会拥有越来越强大的设计、代码、销售等能力。 +**尽早确定目标可以帮助你摆脱诱惑,防止你到最后忘记初心。** 也许你的项目并不能成为下一个 Snapchat, 但是在通往你目标的道路上,你会更加的熟练运用各种做产品的工具。渐渐地,你会拥有越来越强大的设计、编码、销售等能力。 -### 2. 执行计划 +### 2. 保持进展 有一些产品没能完成的原因是作者在半路失去了动力。 **设定一些小目标(比如先赚一个亿);完成这些目标会让你获得前进的动力。** 小的成就累计起来也是很了不起的。 @@ -49,9 +49,9 @@ 比如,我做了 [YC Careers](http://jonathanzwhite.github.io/yc-careers/) 和 [AtomSpace](https://atomspace.co/) 来解决一个产品设计师朋友找工作和面试的烦恼。一夜之间,YC Careers 占领了 [ProductHunt](https://www.producthunt.com/posts/yc-careers) 的榜首,AtomSpace 在上线6个小时之内就拿到了来自陌生人的一百美元订单。 -**你解决越大的痛点,就越容易找到用户。** +**你解决的痛点越大,就越容易找到用户。** -同时,尝试从过个角度分析问题和解决方案。有些成功的点子看起来并没有解决任何问题,但其实不然。比如,Instagram 看起来没有直接解决任何问题。其实不是。Instagram 满足了朋友之间实时社交的需求。如果这个需求不能得到满足,那才是个问题。 +同时,尝试从各个不同的角度寻找解决方案。有些成功的点子看起来并没有解决任何问题,但其实不然。比如,Instagram 看起来没有直接解决任何问题。其实不是。Instagram 满足了朋友之间实时社交的需求。如果这个需求不能得到满足,那才是个问题。 想要了解更多关于这方面内容的话,[John Carmack on Idea Generation](https://amasad.me/carmack) 和 [How to Get Startup Ideas](http://paulgraham.com/startupideas.html) 这两篇文章是非常好的起点。 @@ -67,11 +67,11 @@ ### 5. 寻求帮助 -当你在你的项目上埋头苦干的时候,记得寻求别人的帮助。 **你会很惊喜的发现竟然有这么多人原意去帮助一个陌生人(对,就是你)。** +当你在你的项目上埋头苦干的时候,记得寻求别人的帮助。 **你会很惊喜的发现竟然有这么多人愿意去帮助一个陌生人(对,就是你)。** 我获得的最好的一部分建议就是来自于给别人发邮件或是推特(微博)私信。 -在寻求帮助的时候,请提一些精确、有价值的问题。如果你需要关于设计的一些反馈,就把草稿发给别人;如果你需要营销方面的建议,就详细的列出你尝试过的方法。内容才是关键。如果别人没有回复你,请礼貌的再次询问 —— 有时候别人只是没注意到你的消息而已。 +在寻求帮助的时候,请准备好的你的问题,并尽可能具体。如果你需要关于设计的一些反馈,就把草稿发给别人;如果你需要营销方面的建议,就详细的列出你尝试过的方法。内容才是关键。如果别人没有回复你,请礼貌的再次询问 —— 有时候别人只是没注意到你的消息而已。 --- From b29686077845eda6b54570ae44c68eb629006026 Mon Sep 17 00:00:00 2001 From: xiaoyusilen Date: Sun, 9 Apr 2017 13:03:50 +0800 Subject: [PATCH 085/638] Modify translation --- TODO/anatomy-of-a-function-call-in-go.md | 22 +++++++++++----------- TODO/go-function-calls-redux.md | 6 +++--- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/TODO/anatomy-of-a-function-call-in-go.md b/TODO/anatomy-of-a-function-call-in-go.md index f6141663c05..baca272576d 100644 --- a/TODO/anatomy-of-a-function-call-in-go.md +++ b/TODO/anatomy-of-a-function-call-in-go.md @@ -20,7 +20,7 @@ func add(a, b int) int { } ``` -我们编译的时候需要关闭优化这样方便跟踪每个部分。我们用 `go build -gcflags 'N -l'` 这个命令来完成上述操作。然后我们可以用 `go tool objdump -s main.add func` 输出我们函数的具体细节(这里的 func 是我们的包名,也就是我们刚刚用 go build 编译出的可执行文件)。 +我们编译的时候需要关闭优化,这样方便理解每个部分。我们用 `go build -gcflags 'N -l'` 这个命令来完成上述操作。然后我们可以用 `go tool objdump -s main.add func` 输出我们函数的具体细节(这里的 func 是我们的包名,也就是我们刚刚用 go build 编译出的可执行文件)。 如果你以前从来没有看过汇编语言,那么恭喜啦,你今天看到的内容对你来说是全新的。我将在 Mac 上完成这些事情,所以配置是 Intel 64位。 @@ -36,18 +36,18 @@ func add(a, b int) int { 我们看到了什么?每一行如下所示被分为了4部分: - 源文件的名称和行号(main.go:15)。这行的源代码会被转换为标有代码行号的说明。Go 的一行可能被转换成多行程序集。 -- 对象的偏移量(例如 0x22C0)。 +- 目标文件中的偏移量(例如 0x22C0)。 - 机器码(例如 48c744241800000000)。这是 CPU 实际执行的二进制机器码。我们不需要看这个,几乎没有人看这玩意。 - 机器码的汇编表示形式,这也是我们想要理解的部分。 让我们将注意力集中在最后一部分,汇编语言。 -- MOVQ,ADDQ 和 RET 是说明。它们告诉 CPU 需要执行的操作。后面的参数告诉 CPU 对什么执行该操作。 +- MOVQ,ADDQ 和 RET 是指令。它们告诉 CPU 需要执行的操作。后面的参数告诉 CPU 对什么执行该操作。 - SP,AX 和 CX 是 CPU 寄存器。寄存器是 CPU 用于存储值的地方,CPU 有多个寄存器可以使用。 - SP 是一个专用寄存器,用于存储当前堆栈指针。堆栈是记录局部变量,参数和函数调用的寄存器。每个 goroutine 都有一个堆栈。当一个函数调用另一个函数,然后另一个函数再调用其他函数,每个函数在堆栈上获得自己的存储区域。在函数调用期间创建存储区域,将 SP 的大小中减去所需的存储大小。 -- 0x8(SP)是指超过 SP 指向的内存位置的 8 个字节的内存位置。 +- 0x8(SP)是指超过 SP 指向的存储单元的 8 个字节的存储单元。 -因此,我们的工作的内容包含存储器位置,CPU 寄存器,用于在存储器和寄存器之间移动值的指令以及寄存器上的操作。 这几乎就是一个 CPU 所完成的事情了。 +因此,我们的工作的内容包含存储单元,CPU 寄存器,用于在存储器和寄存器之间移动值的指令以及寄存器上的操作。 这几乎就是一个 CPU 所完成的事情了。 现在让我们从第一条指令开始看每一条内容。别忘了我们需要从内存中加载两个参数 `a` 和 `b`,把它们相加,然后返回至调用函数。 @@ -67,9 +67,9 @@ func add(a, b int) int { 所以我们从中明白了什么: - 好,看起来参数都存在堆栈中,第一个参数存储在 SP+0x8 中,另一个在更高编号的地址中。 -- 并且看上去返回的结果存储在参数后边,一个更高编号的已知地址中。 +- 并且看上去返回的结果存储在参数后边,一个更高编号的地址中。 -现在让我们看另一个函数。这个函数有一个全局变量,不过我们依然会让它看起来很简单。 +现在让我们看另一个函数。这个函数有一个局部变量,不过我们依然会让它看起来很简单。 ``` func add3(a int) int { @@ -108,7 +108,7 @@ func add3(a int) int { 这一行代码似乎没有做什么。所以这可能是一种声明函数的方法。让我们分析一下。 -- `SUBQ $0x10, SP` 从 SP 减去 0x10=16。这个操作为我们分类了 16 字节的堆栈空间。 +- `SUBQ $0x10, SP` 从 SP 减去 0x10=16。这个操作为我们释放了 16 字节的堆栈空间 - `MOVQ BP, 0x8(SP)` 将寄存器 BP 中的值存储至 SP+8 中,然后 `LEAQ 0x8(SP), BP` 将地址 SP+8 中的内容加载到 BP 中。现在我们已经有空间可以存储 BP 中之前所存的内容,然后将 BP 中的内容存储至刚刚分配的存储空间中,这有助于建立堆栈区域链(或者堆栈框架)。这有点神秘,不过在这篇文章中我们恐怕不会解决这个问题。 - 在这一部分的最后是 `MOVQ $ 0x0, 0x20 (SP)`,它和我们刚刚分析的最后一句类似,就是将返回值初始化为0。 @@ -126,7 +126,7 @@ func add3(a int) int { - 调用函数在堆栈中为返回值和参数分配空间。返回值的存储地址比参数的存储地址高。 - 如果被调用函数有局部变量,则通过减少堆栈指针 SP 的值为它们分配空间。它也和寄存器 BP 做了一些神秘的事情。 -- 当函数返回任何 SP&BP 的操作都会相反。 +- 当函数返回任何对 SP 和 BP 的操作都会相反。 让我们看看堆栈在 add3() 方法中如何使用: @@ -145,8 +145,8 @@ SP+0x08: the old value of BP SP+0x0: the local variable b ``` -如果你觉得文章中没有提到 SP+0x10,所以不知道这是干什么用的。我可以告诉你,这是存储返回地址的地方。这是为了让 `RET` 指令知道返回到哪里去。 +如果你觉得文章中没有提到 SP+0x10,所以不*知道*这是干什么用的。我可以告诉你,这是存储返回地址的地方。这是为了让 `RET` 指令知道返回到哪里去。 -这篇文章已经足够了。 希望如果以前你不知道这些东西如何工作,但是现在你觉得你已经有了一些了解,或者如果你被汇编吓倒了,那么它可能不那么清晰。 如果你想了解有关汇编的更多信息,请在评论中告诉我,我会考虑在之后的文章中写出来。 +这篇文章已经足够了。 希望如果以前你不知道这些东西如何工作,但是现在你觉得你已经有了一些了解,或者如果你被汇编吓倒了,那么也许它不那么晦涩难懂了。 如果你想了解有关汇编的更多信息,请在评论中告诉我,我会考虑在之后的文章中写出来。 既然你已经看到这儿了,如果喜欢我的这篇文章或者可以从中学到一点什么的话,那么请给我点个赞这样这篇文章就可以被更多人看到了。 \ No newline at end of file diff --git a/TODO/go-function-calls-redux.md b/TODO/go-function-calls-redux.md index 2e8b6efa515..0fefbdda247 100644 --- a/TODO/go-function-calls-redux.md +++ b/TODO/go-function-calls-redux.md @@ -10,7 +10,7 @@ 什么是调用堆栈?它是一个用于保存局部变量和调用参数的内存区域,并且跟踪每个函数应该返回到哪里去。每个 goroutine 都有它自己的堆栈。你甚至可以说每个 goroutine 就是它自己的堆栈。 -下面是我用于演示堆栈的代码。就是一系列简单的函数调用,main() 函数调用 [f1(0xdeadbeef)](https://en.wikipedia.org/wiki/Hexspeak),然后调用 `f2(0xabad1dea)`,再调用 `f3(0xbaddcafe)`。然后 f3() 将其中一个作为它的参数,并且将它存储在名为 `local` 的本地变量中。然后获取 `local` 的内存地址并且从那里开始输出。因为 `loacl` 在栈内,所以输出的就是栈。 +下面是我用于演示堆栈的代码。就是一系列简单的函数调用,main() 函数调用 [f1(0xdeadbeef)](https://en.wikipedia.org/wiki/Hexspeak),然后调用 `f2(0xabad1dea)`,再调用 `f3(0xbaddcafe)`。然后 `f3()` 将其中一个作为它的参数,并且将它存储在名为 `local` 的本地变量中。然后获取 `local` 的内存地址并且从那里开始输出。因为 `local` 在栈内,所以输出的就是栈。 ```go package main @@ -59,7 +59,7 @@ func showFunc(at uintptr) { } ``` -下面是上述代码的输出结果。它是从 `local` 的地址开始的内存转储,是以十六进制形式展示的8字节列表。左边是每个整数的存储地址,右边是地址内存储的整数。 +下面是上述代码的输出结果。它是从 `local` 的地址开始的内存转储,是以十六进制形式展示的 8 字节列表。左边是每个整数的存储地址,右边是地址内存储的整数。 我们知道 `local` 应该等于 0xBADDCAFE + 1,或者 0xBADDCAFF,这确实是我们转储开始时看到的。 @@ -118,7 +118,7 @@ C42003FFC0: C4200001A0 通过这些我们可以看出: - 首先,堆栈从高地址开始,堆栈地址随着函数调用变小。 -- 当进行函数调用时,调用者将参数放入栈内,然后返回地址(调用函数中的下一条指令的地址),接着指向堆栈中较高的指针。 +- 当进行函数调用时,调用者将参数放入栈内,然后是返回地址(调用函数中的下一条指令的地址),接着是指向堆栈中较高的指针。 - 当调用返回时,这个指针用于在堆栈中查找先前调用的函数。 - 局部变量存储在堆栈指针之后。 From 2668d72d4ad6e182aebb2ba64c3d21c11f69a186 Mon Sep 17 00:00:00 2001 From: xiaoyusilen Date: Sun, 9 Apr 2017 13:07:56 +0800 Subject: [PATCH 086/638] add msg --- TODO/anatomy-of-a-function-call-in-go.md | 2 +- TODO/go-function-calls-redux.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/TODO/anatomy-of-a-function-call-in-go.md b/TODO/anatomy-of-a-function-call-in-go.md index baca272576d..4fa70afdb03 100644 --- a/TODO/anatomy-of-a-function-call-in-go.md +++ b/TODO/anatomy-of-a-function-call-in-go.md @@ -2,7 +2,7 @@ > * 原文作者:[Phil Pearl](https://syslog.ravelin.com/@philpearl?source=post_header_lockup) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 译者:[xiaoyusilen](http://xiaoyu.world) -> * 校对者: +> * 校对者:[1992chenlu](https://github.com/1992chenlu) # 解析 Go 中的函数调用 # diff --git a/TODO/go-function-calls-redux.md b/TODO/go-function-calls-redux.md index 0fefbdda247..b8717411ff1 100644 --- a/TODO/go-function-calls-redux.md +++ b/TODO/go-function-calls-redux.md @@ -2,7 +2,7 @@ > * 原文作者:[Phil Pearl](https://hackernoon.com/@philpearl?source=post_header_lockup) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 译者:[xiaoyusilen](http://xiaoyu.world) -> * 校对者: +> * 校对者:[1992chenlu](https://github.com/1992chenlu) # Go 函数调用 Redux # From 297bd3774a9a46e74b85a6806a36117486b9bf37 Mon Sep 17 00:00:00 2001 From: Tuccuay Date: Sun, 9 Apr 2017 01:56:00 +0800 Subject: [PATCH 087/638] translated mastering swift essential details about string --- ...g-swift-essential-details-about-strings.md | 548 +++++++++--------- 1 file changed, 277 insertions(+), 271 deletions(-) diff --git a/TODO/mastering-swift-essential-details-about-strings.md b/TODO/mastering-swift-essential-details-about-strings.md index b223e0726ef..322df4619dc 100644 --- a/TODO/mastering-swift-essential-details-about-strings.md +++ b/TODO/mastering-swift-essential-details-about-strings.md @@ -1,270 +1,271 @@ > * 原文地址:[Mastering Swift: essential details about strings](https://rainsoft.io/mastering-swift-essential-details-about-strings/) * 原文作者:[Dmitri Pavlutin](https://rainsoft.io/author/dmitri-pavlutin/) * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -* 译者: +* 译者:[Tuccuay](https://www.tuccuay.com) * 校对者: -# Mastering Swift: essential details about strings # +# 掌握 Swift 的字符串细节 -String type is an important component of any programming language. The most useful information that user reads from the window of an iOS application is pure text. +String 类型在任何编程语言中都是一个重要的组成部分。而用户从 iOS 应用的屏幕上能读取到最有效的信息也来自文本。 -To reach a higher number of users, the iOS application must be internationalised and support a lot of modern languages. The Unicode standard solves this problem, but creates additional complexity when working with strings. +为了触及更多的用户,iOS 应用必须国际化以支持大量现代语言。Unicode 标准解决了这个问题,不过这也给我们使用 string 类型带来了额外的挑战性。 -On one hand, the language should provide a good balance between the Unicode complexity and the performance when processing strings. On the other hand, it should provide developer with comfortable structures to handle strings. +从一方面来说,编程语言应该在平衡处理字符串时应该在 Unicode 复杂性和性能之间取得平衡。而另一方面,它需要为开发者提供一个舒适的结构来处理字符串。 -In my opinion, Swift does a great job on both hands. +而在我看来,Swift 在这两方面都做的不错。 -Fortunately Swift's string is not a simple sequence of UTF-16 code units, like in JavaScript or Java. +幸运的是 Swift 的 string 类型并不是像 JavaScript 或者 Java 那样简单的 UTF-16 序列。 -In case of a sequence of UTF-16 code units it's a pain to do Unicode-aware string manipulations: you might break a surrogate pair or combining character sequence. +对一个 UTF-16 码单元序列执行 Unicode 感知的字符串操作是很痛苦的:你可能会打破代理对或组合字符序列。 -Swift implements a better approach. The string itself is not a collection, instead it provides views over the string content that may be applied according to situation. And one particular view, `String.CharacterView`, is fully Unicode-aware. +Swift 对此有着更好的实现方式。字符串本身不再是集合,而是能够根据不同情况为内容提供不同的 view。其中一个特殊的 view: `String.CharacterView` 则是完全支持 Unicode 的。 -For `let myStr = "Hello, world"` you can access the following string views: +对于 `let myStr = "Hello, world"` 来说,你可以访问到下面这些 view: -- `myStr.characters` is `String.CharacterView`. Valuable to access graphemes, that visually are rendered as a single symbol. The most used view. -- `myStr.unicodeScalars` is `String.UnicodeScalarView`. Valuable to access the Unicode code point numbers as 21-bit integers -- `myStr.utf16` is `String.UTF16View`. Useful to access the code unit values encoded in UTF16 -- `myStr.utf8` is `String.UTF8View`. Valuable to access the code unit values encoded in UTF8 +- `myStr.characters` 即 `String.CharacterView`。可以获取字形的值,视觉上呈现为单一的符号。是最常用的试图 +- `myStr.unicodeScalars` 即 `String.UnicodeScalarView`。可以获取 21 整数表示的 Unicode 码位。 +- `myStr.utf16` 即 `String.UTF16View`。用于获取 UTF16 编码的代码单元。 +- `myStr.utf8` 即 `String.UTF8View`。能够获取 UTF8 编码的代码单一。 -![CharacterView, UnicodeScalarView, UTF16View, UTF8View of strings in Swift](https://rainsoft.io/content/images/2016/10/Swift-strings--3-.png) +![Swift 中的 CharacterView, UnicodeScalarView, UTF16View 和 UTF8View](https://rainsoft.io/content/images/2016/10/Swift-strings--3-.png) -Most of the time developer deals with simple string characters, without diving into details like encoding or code units. +在大多数时候开发者都在处理简单的字符串字符,而不是深入到编码或者码位这样的细节中。 -`CharacterView` works nice for most of the string related tasks: iteration over the characters, counting the number of characters, verify substring existence, access by index, different manipulations and so on. +`CharacterView` 能很好的完成大多数任务:迭代字符串、字符计数、验证是否包含字符串、通过索引访问和比较操作等。 -Let's see in more details how these tasks are accomplished in Swift. +让我们看看如何用 Swift 来完成这些任务。 -# 1. Character and CharacterView structures # +# 1. Character 和 CharterView 的结构 -`String.CharacterView` structure is a view over string content that is a collection of `Character`. +`String.CharacterView` 的结构是一个字符内容的视图,它是 `Character` 的集合。 -To access the view from a string, use `characters` string property: +要从字符串访问视图,使用字符的 `characters` 属性: [Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57ff7e018ef62b25bcea2ab1) -``` -let message ="Hello, world" -let characters = message.characters -print(type(of: characters))// => "CharacterView" +```swift +let message = "Hello, world" +let characters = message.characters +print(type(of: characters))// => "CharacterView" ``` -`message.characters` returns the `CharacterView` structure. +`message.characters` 返回了 `CharacterView` 结构. -The character view is a collection of `Character` structures. For example, let's access the first character in a string view: +字符视图是 `Character` 结构的集合。例如我们可以这样来访问字符视图里的第一个字符: [Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57ff7e188ef62b25bcea2ab2) -``` -let message = "Hello, world" -let firstCharacter = message.characters.first! -print(firstCharacter) // => "H" +```swift +let message = "Hello, world" +let firstCharacter = message.characters.first! +print(firstCharacter) // => "H" print(type(of: firstCharacter)) // => "Character" -let capitalHCharacter: Character = "H" +let capitalHCharacter: Character = "H" print(capitalHCharacter == firstCharacter) // => true ``` -`message.characters.first` returns an optional that is the first character `"H"`. +`message.characters.first` 返回了一个可选类型,内容是它的第一个字符 `"H"`. -The character instance represents a single symbol `H`. +这个字符实例代表了单个符号 `H`。 -In Unicode terms `H` is *Latin Capital letter H*, `U+0048` code point. +在 Unicode 标准中,`H` 代表 *Latin Capital letter H*,码位是 `U+0048`。 -Let's go beyond ASCII and see how Swift handles composite symbols. Such characters are rendered as a single visual symbol, but are composed from a sequence of two or more [Unicode scalars](http://unicode.org/glossary/#unicode_scalar_value). Strictly such characters are named **grapheme clusters**. +让我们掠过 ASCII 看看 Swift 如何处理更复杂的符号。这些字符被渲染成单个视觉符号,但实际上是由两个或更多个 Unicode 标量](http://unicode.org/glossary/#unicode_scalar_value) 组成。严格来说这些字符被称为 **字形簇** -*Important*: `CharacterView` is a collection of grapheme clusters of the string. +*Important*: `CharacterView` is a collection of grapheme clusters of the string. +**重点**: `CharacterView` 是字符串的字形簇集合。 -Let's take a closer look at `ç` grapheme. It may be represented in two ways: +让我们看看 `ç` 的字形。他可以有两种表现形式: -- Using `U+00E7` *LATIN SMALL LETTER C WITH CEDILLA*: rendered as `ç` -- Or using a combining character sequence: `U+0063`*LATIN SMALL LETTER C* plus the combining mark `U+0327` *COMBINING CEDILLA*. The grapheme is composite: `c` + `◌̧` = `ç` +- 使用 `U+00E7` *LATIN SMALL LETTER C WITH CEDILLA*:被渲染为 `ç` +- 或者使用组合字符序列:`U+0063`*LATIN SMALL LETTER C* 加上 组合标记 `U + 0327` *COMBINING CEDILLA* 组成复合字形:`c` + `◌̧` = `ç` -Let's pick the second option and see how Swift handles it: +我们看看在第二个选项中 Swift 是如何处理它的: [Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f3466012fc531b0551b918) -``` -let message = "c\u{0327}a va bien" // => "ça va bien" -let firstCharacter = message.characters.first! +```swift +let message = "c\u{0327}a va bien" // => "ça va bien" +let firstCharacter = message.characters.first! print(firstCharacter) // => "ç" -let combiningCharacter: Character = "c\u{0327}" -print(combiningCharacter == firstCharacter) // => true +let combiningCharacter: Character = "c\u{0327}" +print(combiningCharacter == firstCharacter) // => true ``` -`firstCharacter` contains a single grapheme `ç` that is rendered using two Unicode scalars `U+0063` and `U+0327`. +`firstCharacter` 包含了一个字形 `ç`,它是由两个 Unicode 标量 `U+0063` and `U+0327` 组合渲染出来的。 -`Character` structure accepts multiple Unicode scalars as long as they create a single grapheme. If you try to add more graphemes into a single `Character`, Swift triggers an error: +`Character` 结构接受多个 Unicode 标量来创建一个单一的字形。如果你尝试在单个 `Character` 中添加更多的字形,Swift 将会出发错误: -[Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f3510212fc531b0551b91c) +[Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f3510212fc531b0551b91c) -``` -let singleGrapheme: Character = "c\u{0327}\u{0301}" // Works +```swift +let singleGrapheme: Character = "c\u{0327}\u{0301}" // Works print(singleGrapheme) // => "ḉ" -let multipleGraphemes: Character = "ab" // Error! +let multipleGraphemes: Character = "ab" // Error! ``` -Even if `singleGrapheme` is composed of 3 Unicode scalars, it creates a single grapheme `ḉ`. -`multipleGraphemes` tries to create a `Character` from 2 Unicode scalars. This creates 2 separated graphemes `a` and `b` in a single `Character` structure, which is not allowed. +即使 `singleGrapheme` 由 3 个 Unicode 标量组成,它创建了一个字形 `ḉ`。 +而 `multipleGraphemes` 则是从两个 Unicode 标量创建一个 `Character`,这将在单个 `Character` 结构中创建两个分离的字母 `a` 和 `b`,这不是被允许的操作。 -# 2. Iterating over characters in a string # +# 2. 迭代字符串中的字符 -`CharacterView` collection conforms to `Sequence` protocol. This allows to iterate over the view characters in a `for-in` loop: +`CharacterView` 集合遵循了 `Sequence` 协议。这将允许在 `for-in` 循环中便利字符视图: [Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4bc8f27a61152fe7c7410) -``` +```swift let weather ="rain"for char in weather.characters {print(char)}// => "r" // => "a" // => "i" // => "n" ``` -Each character from `weather.characters` is accessed using `for-in` loop. On every iteration `char` variable is assigned with a character from `weather` string: `"r"`, `"a"`, `"i"` and `"n"`. +我们可以在 `for-in` 循环中访问到 `weather.characters` 中的每个字符。`char` 变量将会在迭代中依次分配给 `weather` 中的 `"r"`, `"a"`, `"i"` 和 `"n"` 字符。 -As an alternative, you can iterate over the characters using `forEach(_:)` method, indicating a closure as the first argument: +当然你也可以用 `forEach(_:)` 方法来迭代字符,指定一个闭包作为第一个参数: [Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4bca927a61152fe7c7411) -``` -let weather = "rain" -for char in weather.characters { +```swift +let weather = "rain" +for char in weather.characters { print(char) } -// => "r" -// => "a" -// => "i" +// => "r" +// => "a" +// => "i" // => "n" ``` -The iteration using `forEach(_:)` method is almost the same as `for-in`, only that you cannot use `continue` or `break` statements. +使用 `forEach(_:)` 的方式与 `for-in` 相似,唯一的不同是你不能使用 `continue` 或者 `break` 语句。 -To access the index of the current character in the loop, `CharacterView` provides the `enumerated()` method. The method returns a sequence of tuples `(index, character)`: +要在循环中访问当前字符串的索引可以通过 `CharacterView` 提供的 `enumerated()` 方法。这个方法将会返回一个元组序列 `(index, character)`: [Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4bcd127a61152fe7c7412) -``` -let weather = "rain" -for (index, char) in weather.characters.enumerated() { +```swift +let weather = "rain" +for (index, char) in weather.characters.enumerated() { print("index: \(index), char: \(char)") } -// => "index: 0, char: r" -// => "index: 1, char: a" -// => "index: 2, char: i" -// => "index: 3, char: n" +// => "index: 0, char: r" +// => "index: 1, char: a" +// => "index: 2, char: i" +// => "index: 3, char: n" ``` -`enumerated()` method on each iteration returns tuples `(index, char)`. -`index` variable contains the character index at the current loop step. Correspondingly `char` variable contains the character. +`enumerated()` 方法在每次迭代时返回元组 `(index, char)`。 +`index` 变量即为循环中当前字符的索引,而 `char` 变量则是循环中当前的字符。 -# 3. Counting characters # +# 3. 统计字符 -Simply use `count` property of the `CharacterView` to get the number of characters: +只需要访问 `CharacterView` 的 `counter` 属性就可以获得字符串中字符的个数: -[Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4bcf327a61152fe7c7413) +[Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4bcf327a61152fe7c7413) -``` -let weather ="sunny"print(weather.characters.count)// => 5 +```swift +let weather ="sunny"print(weather.characters.count)// => 5 ``` -`weather.characters.count` contains the number of characters in the string. +`weather.characters.count` 是字符串中字符的个数。 -Each character in the view holds a grapheme. When an adjacent character (for example a [combining mark](http://unicode.org/glossary/#combining_character)) is appended to string, you may find that `count` property is not increased. +视图中的每一个字符都拥有一个字形。当相邻字符(比如 [组合标记](http://unicode.org/glossary/#combining_character) )被添加到字符串时,你可能发现 `count` 属性没有没有变大。 -It happens because an adjacent character does not create a new grapheme in the string, instead it modifies an existing [base Unicode character](http://unicode.org/glossary/#base_character). Let's see an example: +这是因为相邻字符并没有在字符串中创建一个新的字形,而是附加到了已经存在的 [基本 Unicode 字形](http://unicode.org/glossary/#base_character) 中。让我们看一个例子: [Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4bd0927a61152fe7c7414) -``` -var drink = "cafe" -print(drink.characters.count) // => 4 -drink += "\u{0301}" -print(drink) // => "café" -print(drink.characters.count) // => 4 +```swift +var drink = "cafe" +print(drink.characters.count) // => 4 +drink += "\u{0301}" +print(drink) // => "café" +print(drink.characters.count) // => 4 ``` -Initially `drink` has 4 characters. +一开始 `drink` 含有四个字符。 -When the combining mark `U+0301`*COMBINING ACUTE ACCENT* is appended to string, it modifies the previous base character `e` and creates a new grapheme `é`. The property `count` is not increased, because the number of graphemes is still the same. +当组合标记 `U+0301`*COMBINING ACUTE ACCENT* 被添加到字符串中,它改变了上一个基本字符 `e` 并创建了新的字形 `é`。这时属性 `count` 并没有变大,因为字形数量仍然相同。 -# 4. Accessing character by index # +# 4. 按索引访问字符 -Swift doesn't know about the characters count in the string view until it actually evaluates the graphemes in it. As result a subscript that allows to access the character by an integer index directly does not exist. +因为 Swift 直到它实际评估字符视图中的字形之前都不知道字符串中的字符个数。结果就造成了无法通过通过下标的方式访问字符串索引。 -You can access the characters by a special type `String.Index`. +你可以通过特殊的类型 `String.Index` 访问字符。 -If you need to access the first or last characters in the string, the character view structure has `first` and `last` properties: +如果你需要访问字符串中的第一个或者最好一个字符,字符视图结构提供了 `first` 和 `last` 属性: -[Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4bd2027a61152fe7c7415) +[Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4bd2027a61152fe7c7415) -``` -let season = "summer" -print(season.characters.first!) // => "s" -print(season.characters.last!) // => "r" -let empty = "" -print(empty.characters.first == nil) // => true -print(empty.characters.last == nil) // => true +```swift +let season = "summer" +print(season.characters.first!) // => "s" +print(season.characters.last!) // => "r" +let empty = "" +print(empty.characters.first == nil) // => true +print(empty.characters.last == nil) // => true ``` -Notice that `first` and `last` properties are optional type `Character?`. +注意 `first` 和 `last` 属性将会返回可选类型 `Character?`。 -In the empty string `empty` these properties are `nil`. +在空字符串 `empty` 这些属性将会是 `nil`。 ![String indexes in Swift](https://rainsoft.io/content/images/2016/10/Swift-strings--2--1.png) -To get a character at specific position, you have to use `String.Index` type (actually an alias of `String.CharacterView.Index`). String offers a subscript that accepts `String.Index` to access the character, as well as pre-defined indexes `myString.startIndex` and `myString.endIndex`. +要获取特定位置的字符,你必须使用 `String.Index` 类型(实际上是 `String.CharacterView.Index`的别名)。字符提供了一个接受 `String.Index` 下标访问字符的方法,以及预定义的索引 `myString.startIndex` 和 `myString.endIndex`。 -Using string index type, let's access the first and last characters: +让我们使用字符串索引来访问第一个和最后一个字符: [Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4bd3627a61152fe7c7416) -``` -let color = "green" -let startIndex = color.startIndex -let beforeEndIndex = color.index(before: color.endIndex) -print(color[startIndex]) // => "g" -print(color[beforeEndIndex]) // => "n" +```swift +let color = "green" +let startIndex = color.startIndex +let beforeEndIndex = color.index(before: color.endIndex) +print(color[startIndex]) // => "g" +print(color[beforeEndIndex]) // => "n" ``` -`color.startIndex` is the first character index, so `color[startIndex]` evaluates to `g`. -`color.endIndex` indicates the *past the end* position, or simply the position one greater than the last valid subscript argument. To access the last character, you must calculate the index right before string's end index: `color.index(before: color.endIndex)`. +`color.startIndex` 是第一个字符的索引,所以 `color[startIndex]` 表示为 `g`。 +`color.endIndex` 表示**结束**位置,或者简单的说是比最后一个有效小标参数大的位置。要访问最后一个字符,你必须计算它的前一个索引:`color.index(before: color.endIndex)` -To access characters at position by an offset, use the `offsetBy` argument of `index(theIndex, offsetBy: theOffset)` method: +要通过偏移访问字符的位置, 在 `index(theIndex, offsetBy: theOffset)` 方法中使用 `offsetBy` 参数: -[Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4bd4d27a61152fe7c7417) +[Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4bd4d27a61152fe7c7417) -``` -let color = "green" -let secondCharIndex = color.index(color.startIndex, offsetBy: 1) -let thirdCharIndex = color.index(color.startIndex, offsetBy: 2) -print(color[secondCharIndex]) // => "r" -print(color[thirdCharIndex]) // => "e" +```swift +let color = "green" +let secondCharIndex = color.index(color.startIndex, offsetBy: 1) +let thirdCharIndex = color.index(color.startIndex, offsetBy: 2) +print(color[secondCharIndex]) // => "r" +print(color[thirdCharIndex]) // => "e" ``` -Indicating the `offsetBy` argument, you can access the character at specific offset. +指定 `offsetBy` 参数,你将可以放特定偏移量位置的字符。 -Of course `offsetBy` argument is jumping over string graphemes, i.e. the offset applies over `Character` instances of string's `CharacterView`. +当然,`offsetBy` 参数是的步进是字符串的字形。即偏移量适用于 `ChacterView` 中的 `Chacter` 实例。 -If the index is out of range, Swift generates an error: +如果索引超出范围,Swift 会触发错误。 -[Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4bd7227a61152fe7c7418) +[Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4bd7227a61152fe7c7418) -``` +```swift let color ="green" -let oops = color.index(color.startIndex, offsetBy:100)// Error! +let oops = color.index(color.startIndex, offsetBy:100) // Error! ``` -To prevent such situations, indicate an additional argument `limitedBy` to limit the offset: `index(theIndex, offsetBy: theOffset, limitedBy: theLimit)`. The function returns an optional, which is `nil` for out of bounds index: +为了防止这种情况,可以指定一个 `limitedBy` 参数来限制最大偏移量:`index(theIndex, offsetBy: theOffset, limitedBy: theLimit)`。这个函数将会返回一个可选类型,当索引超出范围时将会返回 `nil`: -[Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4bd8d27a61152fe7c7419) +[Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4bd8d27a61152fe7c7419) -``` -let color = "green" -let oops = color.index(color.startIndex, offsetBy: 100, +```swift +let color = "green" +let oops = color.index(color.startIndex, offsetBy: 100, limitedBy: color.endIndex) -if let charIndex = oops { +if let charIndex = oops { print("Correct index") } else { print("Incorrect index") @@ -272,269 +273,274 @@ if let charIndex = oops { // => "Incorrect index" ``` -`oops` is an optional `String.Index?`. The optional unwrap verifies whether the index didn't jump out of the string. +`oops` 是一个可选类型 `String.Index?`。展开可选类型可以验证索引是否超出了字符串的范围。 -# 5. Checking substring existence # +# 5. 检查子串是否存在 -The simplest way to verify the substring existence is to call `contains(_ other: String)` string method: +验证子串是否存在的最简单方法是调用 `contains(_ other: String)` 方法: [Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4bda427a61152fe7c741a) -``` -importFoundationlet animal ="white rabbit"print(animal.contains("rabbit"))// => true print(animal.contains("cat"))// => false +```swift +import Foundation +let animal = "white rabbit" +print(animal.contains("rabbit")) // => true +print(animal.contains("cat")) // => false ``` -`animal.contains("rabbit")` returns `true` because `animal` contains `"rabbit"` substring. +`animal.contains("rabbit")` 将返回 `true` 因为 `animal` 包含了 `"rabbit"` 字符串。 -Correspondingly `animal.contains("cat")` evaluates to `false` for a non-existing substring. +那么当子字串不存在的时候 `animal.contains("cat")` 的值将为 `false`。 -To verify whether the string has specific prefix or suffix, the methods `hasPrefix(_:)` and `hasSuffix(_:)` are available. Let's use them in an example: +要验证字符串是否具有特定的前缀或后缀,可以使用 `hasPrefix(_:)` 和 `hasSuffix(_:)` 方法。我们来看一个例子: -[Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4bdb627a61152fe7c741b) +[Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4bdb627a61152fe7c741b) -``` -importFoundationlet -animal ="white rabbit" -print(animal.hasPrefix("white"))// => true print(animal.hasSuffix("rabbit"))// => true +```swift +importFoundationlet +animal = "white rabbit" +print(animal.hasPrefix("white")) // => true +print(animal.hasSuffix("rabbit")) // => true ``` -`"white"` is a prefix and `"rabbit"` is a suffix of `"white rabbit"`. So the corresponding method calls `animal.hasPrefix("white")` and `animal.hasSuffix("rabbit")` return `true`. +`"white rabbit"` 以 `"white"` 开头并以 `"rabbit"` 结尾。所以我们调用 `animal.hasPrefix("white")` 和 `animal.hasSuffix("rabbit")` 方法都将返回 `true`。 -When you need to search for a particular character, it makes sense to query directly the character view. For example: +当你想搜索字符串时,直接查询字符视图是就可以了。比如: -[Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4bdc827a61152fe7c741c) +[Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4bdc827a61152fe7c741c) -``` -let animal = "white rabbit" -let aChar: Character = "a" -let bChar: Character = "b" -print(animal.characters.contains(aChar)) // => true -print(animal.characters.contains { +```swift +let animal = "white rabbit" +let aChar: Character = "a" +let bChar: Character = "b" +print(animal.characters.contains(aChar)) // => true +print(animal.characters.contains { $0 == aChar || $0 == bChar }) // => true ``` -`contains(_:)` verifies whether the character view has a particular character. +`contains(_:)` 将验证字符视图是否包含指定视图。 -The second function form accepts a closure: `contains(where predicate: (Character) -> Bool)` and performs the same verification. +而第二个函数 `contains(where predicate: (Character) -> Bool)` 则是接受一个闭包并执行验证。 -# 6. String manipulation # +# 6. 字符串操作 -The string in Swift is a *value type*. Whether you pass a string as an argument on function call, assign it to a variable or constant - every time a *copy* of the original string is created. +字符串在 Swift 中是 *value type*(值类型)。无论你是将它作为参数进行函数调用还是将它分配给一个变量或者常量——每次复制都将会创建一个全新的**拷贝**。 -A mutating method call changes the string in place. +所有的可变方法都是在空间内将字符串改变。 -This chapter covers the common manipulations over strings. +本节涵盖了对字符串的常见操作。 -#### Append to string a character or another string #### +#### 附加字符串到另一个字符串 -The simplest way to append to string is `+=` operator. You can append an entire string to original one: +附加字符串较为简便的方法是直接使用 `+=` 操作符。你可以直接将整个字符串附加到原始字符串: -[Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4bddf27a61152fe7c741d) +[Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4bddf27a61152fe7c741d) -``` -var bird ="pigeon" +```swift +var bird ="pigeon" bird +=" sparrow" -print(bird)// => "pigeon sparrow" +print(bird) // => "pigeon sparrow" ``` -String structure provides a mutating method `append()`. The method accepts a string, a character or even a sequence of characters, and appends it to the original string. For instance: +字符串结构提供了一个可变方法 `append()`。该方法接受字符串、字符甚至字符序列,并将其附加到原始字符串。例如 -[Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4bdff27a61152fe7c741e) +[Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4bdff27a61152fe7c741e) -``` -var bird = "pigeon" -let sChar: Character = "s" -bird.append(sChar) -print(bird) // => "pigeons" -bird.append(" and sparrows") -print(bird) // => "pigeons and sparrows" -bird.append(contentsOf: " fly".characters) -print(bird) // => "pigeons and sparrows fly" +```swift +var bird = "pigeon" +let sChar: Character = "s" +bird.append(sChar) +print(bird) // => "pigeons" +bird.append(" and sparrows") +print(bird) // => "pigeons and sparrows" +bird.append(contentsOf: " fly".characters) +print(bird) // => "pigeons and sparrows fly" ``` -#### Extract a substring from string #### +#### 从字符串中截取字符串 -The method `substring()` allows to extract substrings: +使用 `substring()` 方法可以截取字符串: -- from a specific index up to the end of string -- from the the start up to a specific index -- or based on a range of indexes. +- 从特定索引到字符串的末尾 +- 从开头到特定索引 +- 或者基于一个索引区间 -Let's see how it works: +让我们来看看它是如何工作的 -[Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4be1527a61152fe7c741f) +[Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4be1527a61152fe7c741f) -``` - -let plant = "red flower" -let strIndex = plant.index(plant.startIndex, offsetBy: 4) -print(plant.substring(from: strIndex)) // => "flower" +```swift +let plant = "red flower" +let strIndex = plant.index(plant.startIndex, offsetBy: 4) +print(plant.substring(from: strIndex)) // => "flower" print(plant.substring(to: strIndex)) // => "red " -if let index = plant.characters.index(of: "f") { +if let index = plant.characters.index(of: "f") { let flowerRange = index.. "flower" + print(plant.substring(with: flowerRange)) // => "flower" } ``` -The string subscript accepts a range or closed range of string indexes. This helps extracting substrings based on ranges of indexes: +字符串下标接受一个区间或者封闭区间作为字符索引。这有助于根据范围截取子串: [Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4be3127a61152fe7c7420) (target=undefined) -``` -let plant ="green tree"let excludeFirstRange = +```swift +let plant ="green tree"let excludeFirstRange = plant.index(plant.startIndex, offsetBy:1).. "reen tree" let lastTwoRange = - plant.index(plant.endIndex, offsetBy:-2).. "ee" +print(plant[excludeFirstRange]) // => "reen tree" +let lastTwoRange = plant.index(plant.endIndex, offsetBy:-2).. "ee" ``` -#### Insert into string #### +#### 插入字符串 -The string type provides the mutating method `insert()`. The method allows to insert a character or a sequence of characters at specific index. +字符串类型提供了可变方法 `insert()`。此方法可以在特定索引处插入一个字符或者一个字符序列。 -The new character or sequence is inserted before the element currently at the specified index. +新的字符将被插入到指定索引的元素之前。 -See the following sample: +来看一个例子: [Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4be4a27a61152fe7c7421) -``` -var plant = "green tree" -plant.insert("s", at: plant.endIndex) -print(plant) // => "green trees" -plant.insert(contentsOf: "nice ".characters, at: plant.startIndex) -print(plant) // => "nice green trees" +```swift +var plant = "green tree" +plant.insert("s", at: plant.endIndex) +print(plant) // => "green trees" +plant.insert(contentsOf: "nice ".characters, at: plant.startIndex) +print(plant) // => "nice green trees" ``` -#### Remove from string #### +#### 移除字符 -The mutating method `remove(at:)` removes the character at an index: +可变方法 `remove(at:)` 可以删除指定索引处的字符: [Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4be6527a61152fe7c7422) -``` -var weather = "sunny day" -if let index = weather.characters.index(of: " ") { +```swift +var weather = "sunny day" +if let index = weather.characters.index(of: " ") { weather.remove(at: index) print(weather) // => "sunnyday" } ``` -You can remove characters in the string that are in a range of indexes using `removeSubrange(_:)`: +你也可以使用 `removeSubrange(_:)` 来从字符串中移除一个索引区间内的全部字符: [Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4be7b27a61152fe7c7423) -``` -var weather = "sunny day" -let index = weather.index(weather.startIndex, offsetBy: 6) -let range = index.. "sunny" +```swift +var weather = "sunny day" +let index = weather.index(weather.startIndex, offsetBy: 6) +let range = index.. "sunny" ``` -#### Replace in string #### +#### 替换字符串 -The method `replaceSubrange(_:with:)` accepts a range of indexes that should be replaced with a particular string. The method is mutating the string. +`replaceSubrange(_:with:)` 方法接受一个索引区间并可以将区间内的字符串替换为特定字符串。这是字符串的一个可变方法。 -Let's see a sample: +一个简单的例子: [Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4be9327a61152fe7c7424) -``` -var weather = "sunny day" -if let index = weather.characters.index(of: " ") { +```swift +var weather = "sunny day" +if let index = weather.characters.index(of: " ") { let range = weather.startIndex.. "rainy day" } ``` -#### The character view mutation alternative #### +#### 另一些关于字符串的可变操作 -Many of string manipulations described above may be applied directly on string's character view. +上面描述的许多字符串操作都是直接应用于字符串中的字符视图。 -It is a good alternative if you find more comfortable to work directly with a collection of characters. +而更方便的直接使用一个字符序列可能是更好的选择。 -For example you can remove characters at specific index, or directly the first or last characters: +比如你可以删除特定索引出的字符,或者直接删除第一个或者最好一个字符: [Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4bea927a61152fe7c7425) -``` -var fruit = "apple" -fruit.characters.remove(at: fruit.startIndex) -print(fruit) // => "pple" -fruit.characters.removeFirst() -print(fruit) // => "ple" -fruit.characters.removeLast() -print(fruit) // => "pl" +```swift +var fruit = "apple" +fruit.characters.remove(at: fruit.startIndex) +print(fruit) // => "pple" +fruit.characters.removeFirst() +print(fruit) // => "ple" +fruit.characters.removeLast() +print(fruit) // => "pl" ``` -To reverse a word use `reversed()` method of the character view: +使用字符视图中的 `reversed()` 方法来翻转字符视图: [Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4bebf27a61152fe7c7426) -``` + +```swift var fruit ="peach" var reversed =String(fruit.characters.reversed()) -print(reversed)// => "hcaep" +print(reversed)// => "hcaep" ``` -You can easily filter the string: + 你可以很简单得过滤字符串: -[Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4beea27a61152fe7c7427) +[Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4beea27a61152fe7c7427) -``` -let fruit = "or*an*ge" -let filtered = fruit.characters.filter { char in +```swift +let fruit = "or*an*ge" +let filtered = fruit.characters.filter { char in return char != "*" } -print(String(filtered)) // => "orange" +print(String(filtered)) // => "orange" ``` -Map the string content by applying a transformer closure: +Map 可以接受一个闭包来对字符串进行变换: -[Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4befd27a61152fe7c7428) +[Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4befd27a61152fe7c7428) -``` -let fruit = "or*an*ge" -let mapped = fruit.characters.map { char -> Character in +```swift +let fruit = "or*an*ge" +let mapped = fruit.characters.map { char -> Character in if char == "*" { return "+" } return char } -print(String(mapped)) // => "or+an+ge" +print(String(mapped)) // => "or+an+ge" ``` -Or reduce the string content to an accumulator value: +或者使用 reduce 来对字符串来进行一些累加操作: -[Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4bf1d27a61152fe7c7429) (target=undefined) +[Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4bf1d27a61152fe7c7429) -``` -let fruit = "or*an*ge" -let numberOfStars = fruit.characters.reduce(0) { countStars, char in +```swift +let fruit = "or*an*ge" +let numberOfStars = fruit.characters.reduce(0) { countStars, char in if (char == "*") { return countStarts + 1 } return countStars -} -print(numberOfStars) // => 2 +} +print(numberOfStars) // => 2 ``` -# 7. Final words # +# 7. 说在最后 -At first sight, the idea of different types of views over string's content may seem overcomplicated. +首先要说,大家对于字符串内容持有的不同观点看起来似乎过于复杂。 -In my opinion it is a great implementation. Strings can be viewed in different angles: as a collection of graphemes, UTF-8 or UTF-16 code units or simple Unicode scalars. +而在我看来这是一个很好的实现。字符串可以从不同的角度来例假:昨晚字形集合、UTF-8 或 UTF-16 码位和简单是 Unicode 标量。 -Just pick the view depending on your task. In most of the cases it is `CharacterView`. +根据你的任务来选择合适的视图。在大多数情况下,`CharacterView` 都很合适。 -The character view deals with graphemes that may be compound from one or more Unicode scalars. As result the string cannot be integer indexed (like arrays). Instead a special type of index is applicable: `String.Index`. +因为字符视图中可能包含来自一个或多个 Unicode 标量组成的字形。因此字符串并不能像数组那样直接被整数索引。不过可以用特殊的 `String.Index` 来索引字符串。 -Special index type adds a bit of complexity when accessing individual characters or manipulating strings. I agree to pay this price, because having truly Unicode-aware operations on strings is awesome! +虽然特殊的索引类型导致在访问单个字符串或者操作字符串时增加了一些难度。我接受这个成本,因为在字符串上进行诊治的 Unicode 感知操作真的很棒! *Do you find string views comfortable to use? Write a comment bellow and let's discuss!* +*您是否发现使用舒适的字符串视图? 写下评论,让我们讨论一下! +**对于字符操作你有没有找到更舒适的方法?写下评论我们一起来讨论一些吧!** -**P.S.** You might be interested to read my [detailed overview of array and dictionary literals in Swift](https://rainsoft.io/concise-initialization-of-collections-in-swift/). - +**P.S.** 不知道你有没有兴趣阅读我的另一篇文章:[detailed overview of array and dictionary literals in Swift](https://rainsoft.io/concise-initialization-of-collections-in-swift/) \ No newline at end of file From fff51d3ef8a59fb7d27b3141addd5df9f0801668 Mon Sep 17 00:00:00 2001 From: Tuccuay Date: Sun, 9 Apr 2017 15:16:14 +0800 Subject: [PATCH 088/638] fix typo --- TODO/mastering-swift-essential-details-about-strings.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/TODO/mastering-swift-essential-details-about-strings.md b/TODO/mastering-swift-essential-details-about-strings.md index 322df4619dc..325223b5681 100644 --- a/TODO/mastering-swift-essential-details-about-strings.md +++ b/TODO/mastering-swift-essential-details-about-strings.md @@ -537,10 +537,8 @@ print(numberOfStars) // => 2 因为字符视图中可能包含来自一个或多个 Unicode 标量组成的字形。因此字符串并不能像数组那样直接被整数索引。不过可以用特殊的 `String.Index` 来索引字符串。 -虽然特殊的索引类型导致在访问单个字符串或者操作字符串时增加了一些难度。我接受这个成本,因为在字符串上进行诊治的 Unicode 感知操作真的很棒! +虽然特殊的索引类型导致在访问单个字符串或者操作字符串时增加了一些难度。我接受这个成本,因为在字符串上进行真正的 Unicode 感知操作真的很棒! -*Do you find string views comfortable to use? Write a comment bellow and let's discuss!* -*您是否发现使用舒适的字符串视图? 写下评论,让我们讨论一下! **对于字符操作你有没有找到更舒适的方法?写下评论我们一起来讨论一些吧!** **P.S.** 不知道你有没有兴趣阅读我的另一篇文章:[detailed overview of array and dictionary literals in Swift](https://rainsoft.io/concise-initialization-of-collections-in-swift/) \ No newline at end of file From fc3e9aa67f16aee91eade3e94fc777ce289cd439 Mon Sep 17 00:00:00 2001 From: lsvih Date: Sun, 9 Apr 2017 18:04:08 +0800 Subject: [PATCH 089/638] Webpack and Rollup: the same but different --- ...bpack-and-rollup-the-same-but-different.md | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/TODO/webpack-and-rollup-the-same-but-different.md b/TODO/webpack-and-rollup-the-same-but-different.md index 7bc3f470f12..4e286f35689 100644 --- a/TODO/webpack-and-rollup-the-same-but-different.md +++ b/TODO/webpack-and-rollup-the-same-but-different.md @@ -1,57 +1,57 @@ > * 原文地址:[Webpack and Rollup: the same but different](https://medium.com/webpack/webpack-and-rollup-the-same-but-different-a41ad427058c) > * 原文作者:[Rich Harris](https://medium.com/@Rich_Harris?source=post_header_lockup) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 译者: +> * 译者:[lsvih](https://github.com/lsvih) > * 校对者: -# Webpack and Rollup: the same but different # +# 同中有异的 Webpack 与 Rollup # ![](https://cdn-images-1.medium.com/max/1000/1*rtjClMZ8sq3cLFT9Aq8Xyg.png) -This week, Facebook merged a [monster pull request](https://github.com/facebook/react/pull/9327) into React that replaced its existing build process with one based on [Rollup](https://rollupjs.org/) , [prompting](https://twitter.com/stanlemon/status/849366789825994752) [several](https://twitter.com/MrMohtas/status/849362334988595201) [people](https://twitter.com/kyleholzinger/status/849683292760797184) to ask ‘why did you choose Rollup over webpack’? +本周,Facebook 将一个[非常大的 pull request](https://github.com/facebook/react/pull/9327) 合并到了 React 主分支。这个 PR 将 React 以前使用的构建工具替换成了 [Rollup](https://rollupjs.org/)。这让许多人感到不解,纷纷在推特上提问:“为什么你们选择 Rollup 而不选择 Webpack 呢?”[1](https://twitter.com/stanlemon/status/849366789825994752) [2](https://twitter.com/MrMohtas/status/849362334988595201) [3](https://twitter.com/kyleholzinger/status/849683292760797184) -Which is a completely reasonable question. [Webpack](https://webpack.js.org/) is one of the modern JavaScript community’s greatest success stories, with millions of downloads every month powering tens of thousands of websites and applications. It has a large ecosystem, dozens of contributors, and — unusually for a community open source project — [meaningful financial support](https://opencollective.com/webpack) . +有人问这个问题是很正常的。[Webpack](https://webpack.js.org/) 是现在 JavaScript 社区中最伟大的成功传奇之一,它有着数百万/月的下载量,驱动了成千上万的网站与应用。它有着巨大的生态系统、众多的贡献者,并且它与一般的社区开源项目不同——它有着[意义非凡的经济支持](https://opencollective.com/webpack)。 -By comparison, Rollup is a minnow. But React isn’t alone — Vue, Ember, Preact, D3, Three.js, Moment, and dozens of other well-known libraries also use Rollup. So what’s going on? Why can’t we have just one JavaScript module bundler that everyone agrees on? +相比之下,Rollup 是那么的微不足道。但是,除了 React 之外,Vue、Ember、Preact、D3、Three.js、Moment 等众多知名项目都使用了 Rollup。为什么会这样呢?为什么这些项目不使用大家一致认可的 JavaScript 模块打包工具呢? -### A tale of two bundlers ### +### 这两个打包工具的优缺点 ### -webpack was started in 2012 by [Tobias Koppers](https://medium.com/@sokra) to solve a hard problem that existing tools didn’t address: building complex single-page applications (SPAs). Two features in particular changed everything: +Webpack 由 [Tobias Koppers](https://medium.com/@sokra) 在 2012 年创建,用于解决当时的工具不能处理的问题:构建复杂的单页应用(SPA)。它的两个特点改变了一切: -1. **Code-splitting** makes it possible to break your app apart into manageable chunks that can be loaded on-demand, meaning your users get an interactive site much faster than if they had to wait for the whole application to download and parse. You *can* do this manually, but, well… good luck. -2. **Static assets** such as images and CSS can be imported into your app and treated as just another node in the dependency graph. No more carefully placing your files in the right folders and hacked-together scripts for adding hashes to file URLs — webpack can take care of it for you. +1. **代码分割**可以将你的 app 分割成许多个容易管理的分块,这些分块能够在用户使用你的 app 时按需加载。这意味着你的网站可以比那些没有使用此技术的网站要快上很多。因为访问那些网站必须要等待整个应用都被下载并解析完成。当然,你**也可以**自己手动去进行代码分割,但是……总之,祝你好运。 +2. **静态资源**的导入:图片、CSS 等静态资源可以直接导入到你的 app 中,就和其它的模块、节点一样能够进行依赖管理。因此,我们再也不用小心翼翼地将各个静态文件放在特定的文件夹中,然后再去用脚本给文件 URL 加上哈希串了。Webpack 已经帮你完成了这一切。 -Rollup was created for a different reason: to build flat distributables of JavaScript libraries as efficiently as possible, taking advantage of the ingenious design of ES2015 modules. Other module bundlers — webpack included — work by wrapping each module in a function, putting them in a bundle with a browser-friendly implementation of `require`, and evaluating them one-by-one. That’s great if you need things like on-demand loading, but otherwise it’s a bit of a waste, and it [gets worse if you have lots of modules](https://nolanlawson.com/2016/08/15/the-cost-of-small-modules/). +而 Rollup 的开发理念则不同:它利用 ES2015 模块的巧妙设计,尽可能高效地构建精简且易分发的 JavaScript 库。而其它的模块打包器(包括 Webpack在内)都是通过将模块分别封装进函数中,然将这些函数通过能在浏览器中实现的 `require` 方法打包,最后依次处理这些函数。在你需要实现按需加载的时候,这种做法非常的方便,但是这样做引入了很多无关代码,比较浪费资源。当[你有很多模块要打包的时候,这种情况会变得更糟糕](https://nolanlawson.com/2016/08/15/the-cost-of-small-modules/)。 -ES2015 modules enable a different approach, which Rollup uses. All your code is put in the same place and evaluates in one go, resulting in leaner, simpler code that starts up faster. You can [see it for yourself with the Rollup REPL](https://rollupjs.org/repl). +ES2015 模块则启用了一种不同的实现方法,Rollup 用的也就是这种方法。所有代码都将被放置在同一个地方,并且会在一起进行处理。因此得到的最终代码相较而言会更加的精简,运行起来自然也就更快。你可以[点击这儿亲自试试 Rollup 交互式解释器(REPL)](https://rollupjs.org/repl)。 -But there’s a trade-off: code-splitting is a much hairier problem, and at the time of writing Rollup doesn’t support it. Similarly, Rollup doesn’t do hot module replacement (HMR). And perhaps the biggest pain point for people coming to Rollup — while it handles most CommonJS files (via a [plugin](https://github.com/rollup/rollup-plugin-commonjs) ), some things just don’t translate to ES2015, whereas webpack handles everything you throw at it with aplomb. +但这儿也存在一些需要权衡的点:代码分割是一个很棘手的问题,而 Rollup 并不能做到这一点。同样的,Rollup 也不支持模块热替换(HMR)。而且对于打算使用 Rollup 的人来说,还有一个最大的痛点:它通过[插件](https://github.com/rollup/rollup-plugin-commonjs)处理大多数 CommonJS 文件的时候,一些代码将无法被翻译回 ES2015。而与之相反,你可以把这一切的事全部放心交给 Webpack 去处理。 -### So which should I use? ### +### 那么我到底应该选用哪一个呢? ### -By now, hopefully it’s clear why both tools coexist and support each other — they serve different purposes. The tl;dr is this: +回答这个问题之前,我们已经了解了这两个工具各自的优缺点,它们同时存在并相互支持的原因正是因为它们解决的问题不同。那么,现在这个问题的答案简单来说就是: -> Use webpack for apps, and Rollup for libraries +> 在开发应用时使用 Webpack,开发库时使用 Rollup -That’s not a hard and fast rule — lots of sites and apps are built with Rollup, and lots of libraries are built with webpack. But it’s a good rule of thumb. +当然这不是什么严格的规定——有很多的网站和 app 一样是使用 Rollup 构建的,同时也有很多的库使用 Webpack。不过,这是个很值得参考的经验之谈。 -If you need code-splitting, or you have lots of static assets, or you’re building something with lots of CommonJS dependencies, Webpack is a better choice. If your codebase is ES2015 modules and you’re making something to be used by other people, you probably want Rollup. +如果你需要进行代码分割,或者你有很多的静态资源,再或者你做的东西深度依赖 CommonJS,毫无疑问 Webpack 是你的最佳选择。如果你的代码基于 ES2015 模块编写,并且你做的东西是准备给他人使用的,你应该更适合用 Rollup。 -### Package authors: use `pkg.module!` ### +### 对于包作者的建议:请使用 `pkg.module`! ### -For a long time, using JavaScript libraries was a bit of a crapshoot, because you and the library author effectively had to agree on a module system. If you were using Browserify but she preferred AMD, you would have to duct tape things together before you could actually build anything. The [Universal Module Definition](https://github.com/umdjs/umd) (UMD) format *sort of* fixed that, but because it wasn’t enforced anywhere you never knew quite what you were going to get. +在很长一段时间里,使用 JavaScript 库是一件相当有风险的事,因为这意味着你必须和库的作者在模块系统上的意见保持一致。如果你使用 Browserify 而他更喜欢 AMD,你就不得不在 build 之前先强行将两者粘起来。[通用模块定义(UMD)](https://github.com/umdjs/umd)格式对这个问题进行了 *部分* 的修复,但是它没有强制要求所有的人都用它,最后反而会让你更加不知所措。 -ES2015 changes all that, because `import` and `export` are part of the language. In the future, there’ll be no ambiguity, and things will work a lot more seamlessly. Unfortunately, because browsers (mostly) and Node don’t yet support `import` and `export`, we still need to ship UMD files (or CommonJS, if you’re building something Node-only). +ES2015 改变了这一切,因为 `import` 与 `export` 就是语言规范本身的一部分。在未来,不再会有现在这种模棱两可的情况,所有东西都将更加无缝地配合工作。不幸的是,由于大多数浏览器和 Node 还不支持 `import` 和 `export`,我们仍然需要依靠 UMD 规范(如果你只写 Node 的话也可以用 CommonJS)。 -By adding a `"module": "dist/my-library.es.js"` entry to your library’s package.json file (aka `pkg.module`), it’s possible to serve UMD and ES2015 at the same time, right now. **That’s important because Webpack and Rollup can both use** `pkg.module` **to generate the most efficient code possible** — in some cases, they can even both [tree-shake](https://webpack.js.org/guides/tree-shaking/) unused parts of your library away. +现在给你的库的 package.json 文件增加一个 `"module": "dist/my-library.es.js"` 入口,可以让你的库同时支持 UMD 与 ES2015。**这很重要,因为 Webpack 和 Rollup 都使用了 ** `pkg.module` ** 来尽可能的生成效率更高的代码**——在一些情况下,它们都能使用 [tree-shake](https://webpack.js.org/guides/tree-shaking/) 来精简掉你的库中未使用的部分。 -*Learn more about`pkg.module` on the [*Rollup wiki*](https://github.com/rollup/rollup/wiki/pkg.module) .* +*了解更多有关 `pkg.module` 的内容请访问 [Rollup wiki](https://github.com/rollup/rollup/wiki/pkg.module) 。* -Hopefully this article makes the relationship between the two projects a bit clearer. If you still have questions, find us on Twitter at [rich_harris](https://twitter.com/rich_harris)/[rollupjs](https://twitter.com/rollupjs) and [thelarkinn](https://twitter.com/thelarkinn) . Happy bundling! +希望这篇文章能让你理清这两个开源项目之间的关系。如果你还有问题,可以在推特联系[rich_harris](https://twitter.com/rich_harris)、[rollupjs](https://twitter.com/rollupjs)、[thelarkinn](https://twitter.com/thelarkinn)。祝你打包快乐! -Our thanks to Rich Harris for writing this article. We believe that collaboration in open source is incredibly vital to ensure we push technology and the web forward together. +感谢 Rich Harris 写了这篇文章。我们坚信开源协作是共同促进 web 技术前进的重要动力。 -No time to help contribute? Want to give back in other ways? Become a Backer or Sponsor to webpack by [donating to our open collective](https://opencollective.com/webpack). Open Collective not only helps support the Core Team, but also supports contributors who have spent significant time improving our organization on their free time! ❤ +没有时间为开源项目做贡献?想要以其它方式回馈吗?欢迎通过 [Open Collective 进行捐赠](https://opencollective.com/webpack),成为 Webpack 的支持者或赞助商。Open Collective 不仅会资助核心团队,而且还会资助那些贡献出空闲时间帮助我们改进项目的贡献者们。 --- From 117db55d80428317dd5efa2f50cb612546f5a361 Mon Sep 17 00:00:00 2001 From: gy134340 Date: Sun, 9 Apr 2017 18:05:38 +0800 Subject: [PATCH 090/638] =?UTF-8?q?=E4=B8=BA=E4=BB=80=E4=B9=88=E7=94=A8=20?= =?UTF-8?q?JavaScript=20=E5=AD=A6=E4=B9=A0=E5=87=BD=E6=95=B0=E5=BC=8F?= =?UTF-8?q?=E7=BC=96=E7=A8=8B=EF=BC=9F=EF=BC=88=E7=BB=84=E6=88=90=E5=8C=96?= =?UTF-8?q?=E8=BD=AF=E4=BB=B6=EF=BC=89=EF=BC=88=E7=AC=AC=E4=BA=8C=E9=83=A8?= =?UTF-8?q?=E5=88=86=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...amming-in-javascript-composing-software.md | 157 +++++++++--------- 1 file changed, 78 insertions(+), 79 deletions(-) diff --git a/TODO/why-learn-functional-programming-in-javascript-composing-software.md b/TODO/why-learn-functional-programming-in-javascript-composing-software.md index dd82cf42a4a..3203cee2ce5 100644 --- a/TODO/why-learn-functional-programming-in-javascript-composing-software.md +++ b/TODO/why-learn-functional-programming-in-javascript-composing-software.md @@ -4,152 +4,151 @@ > * 译者: > * 校对者: -# Why Learn Functional Programming in JavaScript? (Composing Software) +# 为什么用 JavaScript 学习函数式编程?(组成化软件)(第二部分) -Smoke Art Cubes to Smoke — MattysFlicks — (CC BY 2.0) -> Note: This is part of the “Composing Software” series on learning functional programming and compositional software techniques in JavaScript ES6+ from the ground up. Stay tuned. There’s a lot more of this to come! -> [Start over at Part 1](https://medium.com/javascript-scene/the-rise-and-fall-and-rise-of-functional-programming-composable-software-c2d91b424c8c#.2dfd6n6qe) | [Next >](https://medium.com/javascript-scene/a-functional-programmers-introduction-to-javascript-composing-software-d670d14ede30#.2e4youss2) +烟雾的方块艺术 —MattysFlicks —(CC BY 2.0) +> 注意:这是从基础学习函数式编程和使用 JavaScript ES6+ 组成软件的第一部分。保持关注,接下来还有很多! +> [从第一部分开始](https://medium.com/javascript-scene/the-rise-and-fall-and-rise-of-functional-programming-composable-software-c2d91b424c8c#.2dfd6n6qe) | [接下来的 >](https://medium.com/javascript-scene/a-functional-programmers-introduction-to-javascript-composing-software-d670d14ede30#.2e4youss2) -Forget whatever you think you know about JavaScript, and approach this material with a beginner’s mind. To help you do that, we’re going to review the JavaScript basics from the ground up, as if you’ve never seen JavaScript before. If you’re a beginner, you’re in luck. Finally something exploring ES6 and functional programming from scratch! Hopefully all the new concepts are explained along the way — but don’t count on too much pampering. +忘了你认为知道的关于 JavaScript 的一切,用初学者的眼光去看待它。为了帮助你做到这一点,我们将会从头复习一下 JavaScript 的基础,就像你与其尚未谋面一样。如果你是初学者,那你就很幸运了。最终从零开始探索 ES6 和函数式编程!希望所有的概念都被解释清楚 — 但不要太依赖于此。 -If you’re a seasoned developer already familiar with JavaScript, or a pure functional language, maybe you’re thinking that JavaScript is a funny choice for an exploration of functional programming. Set those thoughts aside, and try to approach the material with an open mind. You may find that there is another level to JavaScript programming. One you never knew existed. +如果你是已经熟悉 JavaScript 或者纯函数式语言的老开发者了,也许你会认为 JavaScript 是探索函数式编程有趣的选择。把这些想法放在一边,用更开放的思想接触它,你会发现 JavaScript 编程更高层次的东西。一些你从来不知道的东西。 -Since this text is called “Composing Software”, and functional programming is the obvious way to compose software (using function composition, higher order functions, etc…), you may be wondering why I’m not talking about Haskell, ClojureScript, or Elm, instead of JavaScript. +由于这个被称为“组成式软件”,同时函数式编程是明显的组成软件的方法(使用函数组合,高阶函数等等),你也许想知道为什么我不用 Haskell, ClojureScript,或者 Elm,而是 JavaScript。 -JavaScript has the most important features needed for functional programming: +JavaScript 有函数式编程所需要的最重要的特性: -1. **First class functions:** The ability to use functions as data values: pass functions as arguments, return functions, and assign functions to variables and object properties. This property allows for higher order functions, which enable partial application, currying, and composition. -2. **Anonymous functions and concise lambda syntax:**`x => x * 2` is a valid function expression in JavaScript. Concise lambdas make it easier to work with higher-order functions. -3. **Closures:** A closure is the bundling of a function with its lexical environment. Closures are created at function creation time. When a function is defined inside another function, it has access to the variable bindings in the outer function, even after the outer function exits. Closures are how partial applications get their fixed arguments. A fixed argument is an argument bound in the closure scope of a returned function. In `add2(1)(2)`, `1` is a fixed argument in the function returned by `add2(1)`. +1. **一级函数:**使用函数作为数据值的能力:用函数传参,返回函数,用函数做变量和对象属性。这个属性允许更高级别的函数,使局部应用、柯里化和组合成为可能。 +2. **匿名函数和简介的 lambda 语法:**`x => x * 2` 是 JavaScript 中有效的函数表达式。简介的 lambda 语法使得它更好的跟高阶函数合作。 +3. **闭包:**闭包是一个有着自己独立作用域的捆绑函数。闭包在函数被创建时被创建。当一个函数在另一个函数内部被创建,它可以访问外部函数的变量,即使在外部函数退出后。闭包时使局部应用或者固定的参数。固定的参数时绑定在返回函数的作用域范围内的参数。在 `add2(1)(2)` 中,`1` 是 `add2(1)` 返回的函数中的固定参数。 -### What JavaScript is Missing +### JavaScript 缺少了什么 -JavaScript is a multi-paradigm language, meaning that it supports programming in many different styles. Other styles supported by JavaScript include procedural (imperative) programming (like C), where functions represent a subroutine of instructions that can be called repeatedly for reuse and organization, object-oriented programming, where objects — not functions — are the primary building blocks, and of course, functional programming. The disadvantage of a multi-paradigm language is that imperative and object-oriented programming tend to imply that almost everything needs to be mutable. +JavaScript 是多范式语言,意味着它支持多种风格的编程。其他被 JavaScript 支持的风格包括过程式的(命令式)编程(比如 C),把函数看作可以被重复调用和组织的子程序指令;面向对象编程,对象 — 而不是函数 — 作为初始构造块;当然,还有函数式编程。多范式编程语言的劣性在于命令式和面向对象往往意味着所有东西都是可变的。 -Mutation is a change to data structure that happens in-place. For example: +可变性指的是数据结构上的变化。比如: + + const foo = { + bar: 'baz' + }; - const foo = { - bar: 'baz' - }; + foo.bar = 'qux'; // mutation - foo.bar = 'qux'; // mutation +对象通常需要可变性以便于被方法更新值,在命令式的语言中,大部分的数据结构可变以便于数组和对象的高效操作。 -Objects usually need to be mutable so that their properties can be updated by methods. In imperative programming, most data structures are mutable to enable efficient in-place manipulation of objects and arrays. +下面是一些函数式语言拥有但是 JavaScript 没有的特性: -Here are some features that some functional languages have, that JavaScript does not have: +1. **纯粹性:**在一些函数式语言中,纯粹性是强制的,有副作用的表达式是不被允许的。 +2. **不可变性:**一些函数式语言不允许转变,采用表达式来产生新的数据结构来代替更改一个已存的数据结构,比如说数组或者对象。这样看起来可能不够搞笑,但是大多数函数式语言在引擎下使用 trie 数据结构,具有结构共享的特点:意味着旧的对象和新的对象是对相同数据的引用。 +3. **递归:**递归是函数引用自身来进行迭代的能力。在大多数函数式语言中,递归是迭代的唯一方式,它们没有像 `for` 、`while`、`do` 这类循环语句。 -1. **Purity:** In some FP languages, purity is enforced by the language. Expressions with side-effects are not allowed. -2. **Immutability:** Some FP languages disable mutations. Instead of mutating an existing data structure, such as an array or object, expressions evaluate to new data structures. This may sound inefficient, but most functional languages use trie data structures under the hood, which feature structural sharing: meaning that the old object and new object share references to the data that is the same. -3. **Recursion:** Recursion is the ability for a function to reference itself for the purpose of iteration. In many FP languages, recursion is the only way to iterate. There are no loop statements like `for`, `while`, or `do` loops. +**纯粹性:**在 JavaScript 中,纯粹性由约定来达成,如果你不是使用纯函数来构成你的大多数应用,那么你就不是在进行函数式风格的编程。很不幸,在 JavaScript 中,你很容易就会不小心创建和使用一些不纯的函数。 -**Purity:** In JavaScript, purity must be achieved by convention. If you’re not building most of your application by composing pure functions, you’re not programming using the functional style. It’s unfortunately easy in JavaScript to get off track by accidentally creating and using impure functions. +**不可变性:**在纯函数式语言中,可变性通常是强制的,JavaScript 缺少函数式语言中高效的、基于 trie 树的数据结构,但是又一些你可以使用的库,包括 [Immutable.js](https://facebook.github.io/immutable-js/) 和 [Mori](https://github.com/swannodette/mori),真是期望未来的 ECMAScript 规范版本可以拥抱不可变数据结构。 -**Immutability:** In pure functional languages, immutability is often enforced. JavaScript lacks efficient, immutable trie-based data structures used by most functional languages, but there are libraries that help, including [Immutable.js](https://facebook.github.io/immutable-js/) and [Mori](https://github.com/swannodette/mori). I’m hoping that future versions of the ECMAScript spec will embrace immutable data structures. +有一些未来的迹象,比如说在 ES6 中添加了 `const` 关键字,`const` 声明的变量不能被重新赋值,知道 `const` 并不实际代表不可改变的值也很重要。 -There are signs that offer hope, like the addition of the `const` keyword in ES6. A name binding defined with `const` can't be reassigned to refer to a different value. It's important to understand that `const` does not represent an immutable *value.* +`const` 声明的对象不能被重新声明为新的对象,但是对象的属性却是可变的,JavaScript 有 `freeze()` 对象的能力,但是这些对象只能在根实例上被冻结,意味着嵌套着的对象还是可以改变它的属性。换句话说,在 JavaScript 规范中看到真正的不可变还有很多路要走。 -A `const` object can't be reassigned to refer to a completely different object, but the object it refers to *can have its properties mutated*. JavaScript also has the ability to `freeze()` objects, but those objects are only frozen at the root level, meaning that a nested object can still have properties of its properties mutated. In other words, there's still a long road ahead before we see true composite immutables in the JavaScript specification. +**递归:**JavaScript 支持递归,但是大多数函数式语言都有尾部调用优化的特性,尾部调用优化是一个允许递归的函数重用堆栈帧来递归调用的特性。 -**Recursion:** JavaScript technically supports recursion, but most functional languages have a feature called tail call optimization. Tail call optimization is a feature which allows recursive functions to reuse stack frames for recursive calls. +没有尾部调用优化,一个调用的栈很可能没有边界导致堆栈溢出。JavaScript 在 ES6 规范中有一个有限的尾部调用优化。不幸的是,只有一个主要的浏览器引擎支持它,这个优化被部分应用随后从 Babel(最流行的 JavaScript 编译器,在旧的浏览器中被用来把 ES6 编译到 ES5)。 -Without tail call optimization, a call stack can grow without bounds and cause a stack overflow. JavaScript technically got a limited form of tail call optimization in the ES6 specification. Unfortunately, only one of the major browser engines implemented it, and the optimization was partially implemented and then subsequently removed from Babel (the most popular standard JavaScript compiler, used to compile ES6 to ES5 for use in older browsers). +最后一行:现在使用递归来作为大的迭代还不是很安全 — 即使你很小心的调用尾部的函数。 -Bottom line: It still isn’t safe to use recursion for large iterations — even if you’re careful to call the function in the tail position. +### 什么又是 JavaScript 所有但是纯函数式语言没有的 -### What JavaScript Has that Pure Functional Languages Lack +一个纯粹主义者会告诉你 JavaScript 的可变性是它的重要缺点,这是事实。但是,引起的副作用和改变有时候很有用。事实上,不可能在规避所有副作用的情况下开发有用的现代应用。纯函数式语言比如说 Haskell 使用副作用,但是使用 monads 包来伪装纯函数,从而使程序保持纯净,尽管用 monads 所带来的副作用是不纯净的。 -A purist will tell you that JavaScript’s mutability is its major disadvantage, which is true. However, side effects and mutation are sometimes beneficial. In fact, it’s impossible to create most useful modern applications without side effects. Pure functional languages like Haskell use side-effects, but camouflage them from pure functions using boxes called monads, allowing the program to remain pure even though the side effects represented by the monads are impure. +monads 的问题是,尽管它的使用很简单,但是对一个不是很熟悉它的人解释清楚它有点像对牛谈琴。 -The trouble with monads is that, even though their use is quite simple, explaining what a monad is to somebody unfamiliar with lots of examples is a bit like explaining what the color “blue” looks like to a blind person. +> “monad 是 endofunctor 范畴的幺半群,有什么问题?” ~James Iry 所引用 Philip Wadler的话,解释一个 Saunders Mac Lane 说过的名言。[*“A Brief, Incomplete, and Mostly Wrong History of Programming Languages”*](http://james-iry.blogspot.com/2009/05/brief-incomplete-and-mostly-wrong.html) -> “A monad is a monoid in the category of endofunctors, what’s the problem?” ~ James Iry, fictionally quoting Philip Wadler, paraphrasing a real quote by Saunders Mac Lane.[*“A Brief, Incomplete, and Mostly Wrong History of Programming Languages”*](http://james-iry.blogspot.com/2009/05/brief-incomplete-and-mostly-wrong.html) +典型的,这是在调侃这有趣的一点。在上面的引用中,关于 monads 的解释相比最初的有了很大的简化,原来是下面这样: -Typically, parody exaggerates things to make a funny point funnier. In the quote above, the explanation of monads is actually *simplified* from the original quote, which goes like this: +> “`X` 中的 monad 是其 endofunctor 范畴的幺半群,生成 endofunctor 和被 endofunctor 单位 set 组合所代替的 `X` ” ~ Saunders Mac Lane。 [*"Categories for the Working Mathematician"*](https://www.amazon.com/Categories-Working-Mathematician-Graduate-Mathematics/dp/0387984038//ref=as_li_ss_tl?ie=UTF8&linkCode=ll1&tag=eejs-20&linkId=de6f23899da4b5892f562413173be4f0) -> “A monad in `X` is just a monoid in the category of endofunctors of `X`, with product `×` replaced by composition of endofunctors and unit set by the identity endofunctor." ~ Saunders Mac Lane. [*"Categories for the Working Mathematician"*](https://www.amazon.com/Categories-Working-Mathematician-Graduate-Mathematics/dp/0387984038//ref=as_li_ss_tl?ie=UTF8&linkCode=ll1&tag=eejs-20&linkId=de6f23899da4b5892f562413173be4f0) +尽管这样,在我的观点看来,害怕 monads 是没有必要的,学习 monads 最好的方法不是去读关于它的一堆书和博客,而是立刻去使用它。对于大部分的函数式编程语言来说,晦涩的学术词汇比它实际概念难的多,相信我,你不必通过了解 Saunders Mac Lane 来了解函数式编程。 -Even so, in my opinion, fear of monads is weak reasoning. The best way to learn monads is not to read a bunch of books and blog posts on the subject, but to jump in and start using them. As with most things in functional programming, the impenetrable academic vocabulary is much harder to understand than the concepts. Trust me, you don’t have to understand Saunders Mac Lane to understand functional programming. +尽管它不是对所有的编程风格都完全的理想,JavaScript 无疑是作为适应各种编程风格和背景的人的通用编程语言被设计出来的。 -While it may not be absolutely ideal for every programmming style, JavaScript is unapologetically a general-purpose language designed to be usable by various people with various programming styles and backgrounds. +根据 [Brendan Eric](https://brendaneich.com/2008/04/popularity/) 所言,在一开始的时候,网景公司就有意适应两类开发者: -[According to Brendan Eich](https://brendaneich.com/2008/04/popularity/), this was intentional from the beginning. Netscape had to support two kinds of programmers: +> “...写组件的,比如说 C++ 或者 Java;写脚本的、业余的和爱好者,比如直接写嵌在 HTML 里的代码的。” -> *“…the component authors, who wrote in C++ or (we hoped) Java; and the ‘scripters’, amateur or pro, who would write code directly embedded in HTML.”* +最初,网景公司的意向是支持两种不同的语言,同时脚本语言大致要像 Scheme (一个 Lisp 的方言),而且,Brendan Eich: -Originally, the intent was that Netscape would support two different languages, and the scripting language would probably resemble Scheme (a dialect of Lisp). Again, Brendan Eich: +> “我被招聘到网景公司,目的是在浏览器中 **做一些 Scheme**”。 -> *“I was recruited to Netscape with the promise of ‘doing Scheme’ in the browser.”* +JavaScript 应当是一门新的语言: -JavaScript had to be a new language: +> “上面工程管理的命令是这门语言**应当像 Java**,这就排除了 Perl,Python,和 Tcl,以及 Scheme。” -> *“The *diktat* from upper engineering management was that the language must ‘look like Java’. That ruled out Perl, Python, and Tcl, along with Scheme.”* +所以,Brendan Eich 最初脑子里的想法是: -So, the ideas in Brendan Eich’s head from the beginning were: +1. 浏览器中的 Scheme。 +2. 看起来像 Java。 -1. Scheme in the browser. -2. Look like Java. +最终的结果稍微有点复杂: -It ended up being even more of a mish-mash: +>“我不骄傲,但我很高兴我选择了 Scheme 的一类函数和 Self(尽管奇怪)的原型作为主要的元素。”由于 Java 的影响,特别是 y2k 的 Date 问题以及对象的区别(比如string 和 String),就不幸了。” -> *“I’m not proud, but I’m happy that I chose Scheme-ish first-class functions and Self-ish (albeit singular) prototypes as the main ingredients. The Java influences, especially y2k Date bugs but also the primitive vs. object distinction (e.g., string vs. String), were unfortunate.”* +我列出了这些 “不好的” 的类 Java 特性,最后转换成 JavaScript: -I’d add to the list of “unfortunate” Java-like features that eventually made their way into JavaScript: +* 构造函数和 `new` 关键子,跟工厂函数有着不同的调用和使用语义。 +* `class` 的关键字和单一父类 `extends` 作为最初的继承机制。 +* 用户更习惯于把 `class` 看作是它的静态类型(实际并非如此)。 -- Constructor functions and the `new` keyword, with different calling and usage semantics from factory functions. -- A `class` keyword with single-ancestor `extends` as the primary inheritance mechanism. -- The user’s tendency to think of a `class` as if it's a static type (it's not). +我的意见:永远避免使用这些东西。 -My advice: Avoid those whenever you can. +很幸运 JavaScript 成为了这样厉害的语言,因为事实上证明脚本的方式赢了那些建立在“组件”上的方式(现在,Java、Flash、和 ActiveX 扩展已经不被大部分安装的浏览器支持)。 -We’re lucky that JavaScript ended up being such a capable language, because it turns out that the scripting approach won over the “component” approach (today, Java, Flash, and ActiveX extensions are unsupported in huge numbers of installed browsers). +我们最终创作了一个直接被浏览器支持的语言:JavaScript。 -What we eventually ended up with was one language directly supported by the browser: JavaScript. +那意味着浏览器可以减少臃肿和问题,因为它们现在只需要支持一种语言:JavaScript。你也许认为 WebAssembly 是异常,但是 WebAssembly 设计之初的目的是使用兼容的抽象语法树来共享JavaScript的语言绑定(AST)。事实上,最早的把 WebAssembly 编译成 JavaScript 的子集的示范是 ASM.js。 -That means that browsers are less bloated and less buggy, because they only need to support a single set of language bindings: JavaScript’s. You might be thinking that WebAssembly is an exception, but one of the design goals of WebAssembly is to share JavaScript’s language bindings using a compatible Abstract Syntax Tree (AST). In fact, the first demonstrations compiled WebAssembly to a subset of JavaScript known as ASM.js. +作为 web 平台唯一的通用标准编程语言,JavaScript 在软件历史潮流中乘风直上: -The position as the only standard general purpose programming language for the web platform allowed JavaScript to ride the biggest language popularity wave in the history of software: +App 吞食世界, web 吞食 app, 同时 JavaScript 吞食 web。 -Apps ate the world, the web ate apps, and JavaScript ate the web. +根据[各方](http://redmonk.com/sogrady/2016/07/20/language-rankings-6-16/)[调查](http://stackoverflow.com/research/developer-survey-2016),[JavaScript](https://octoverse.github.com/)是目前世界上最流行的语言。 -By [multiple](http://redmonk.com/sogrady/2016/07/20/language-rankings-6-16/)[measures](http://stackoverflow.com/research/developer-survey-2016), [JavaScript](https://octoverse.github.com/) is now the most popular programming language in the world. +JavaScript 并不是函数式编程的理想化工具,但是它却是为大型的分布式的团队开发大型应用的好工具,因为不同的团队也许有不同建立应用的想法。 -JavaScript is not the ideal tool for functional programming, but it’s a great tool for building large applications on very large, distributed teams, where different teams may have different ideas about how to build an application. +一些团队致力于脚本化,那么命令式的编程就特别有用,另外一些更精于抽象架构,那么一点保留的面向对象方法也许不失为坏。还有一些拥抱函数式编程,使用纯函数来确保稳定性、可测试性和项目状态管理以便减少用户的反馈。团队里的这些人可以使用相同的语言,意味着他们可以更好的交换想法,互相学习和在其他人的基础上更进一步的开发。 -Some teams may concentrate on scripting glue, where imperative programming is particularly useful. Others may concentrate on building architectural abstractions, where a bit of (restrained, careful) OO thinking may not be a bad idea. Still others may embrace functional programming, reducing over user actions using pure functions for deterministic, testable management of application state. Members on these teams are all using the same language, meaning that they can more easily exchange ideas, learn from each other, and build on each other’s work. +在 JavaScript 中,所有这些想法可以共存,这样就让更多的人开始拥抱 JavaScript,然后就产生了[世界上最大的开源包管理器](http://www.modulecounts.com/) (2017 年 2 月),[npm](https://www.npmjs.com/)。 -In JavaScript, all of these ideas can co-exist, which allows more people to embrace JavaScript, which has led to the [largest open-source package registry in the world](http://www.modulecounts.com/) (as of February, 2017), [npm](https://www.npmjs.com/). +JavaScript 的真正优势在于其生态系统中的思想和用户的多样性。它也许不是纯函数式编程的理想语言,但它是你可以想象的工作在不同平台的人共同合作的理想语言,比如说 Java、Lisp 或者 C。JavaScript 也许对这些有这些背景的用户完全友好,但是这些人很乐意学习这门语言并迅速投入生产。 -The true strength of JavaScript is diversity of thought and users in the ecosystem. It may not be absolutely the ideal language for functional programming purists, but it may be the ideal language for working together using one language that works on just about every platform you can imagine — familiar to people coming from other popular languages such as Java, Lisp, or C. JavaScript won’t feel ideally comfortable to users with any of those backgrounds, but they may feel *comfortable enough* to learn the language and become productive quickly. +我同意 JavaScript 并不是函数式编程着最好的语言。但是,没有任何其他语言可以声称他们可以被所有人使用,同时正如 ES6 所述:JavaScript 可以满足到更与喜欢函数式编程的人的需要,同时也越来越好。相比于抛弃 JavaScript 和它不可思议的被世界上所有公司使用的生态系统,为什么不拥抱它,把它变成一个更适合软件组成化的语言? -I agree that JavaScript is not the best language for functional programmers. However, no other functional language can claim that it is a language that everybody can use and embrace, and as demonstrated by ES6: JavaScript can and does get better at serving the needs of users interested in functional programming. Instead of abandoning JavaScript and its incredible ecosystem used by virtually every company in the world, why not embrace it, and make it a better language for software composition incrementally? +现在,JavaScript 已经是一门足够优秀的函数式编程语言,意味着人们可以使用 JavaScript 的函数式编程方法来构造所有有趣的和有用的东西。Netflix(和其他使用 Angular 2+ 的应用)使用基于 RxJS 的函数式功能。[Facebook](https://github.com/facebook/react/wiki/sites-using-react)在 React 中使用纯函数、高阶函数和高级组件来开发 Facebook 和 Instagram,[PayPal, KhanAcademy, and Flipkart](https://github.com/reactjs/redux/issues/310)使用 Redux 来进行状态管理。 -As-is, JavaScript is already a *good enough* functional programming language, meaning that people are building all kinds of useful and interesting things in JavaScript, using functional programming techniques. Netflix (and every app built with Angular 2+) uses functional utilities based on RxJS. [Facebook](https://github.com/facebook/react/wiki/sites-using-react) uses the concepts of pure functions, higher-order functions, and higher order components in React to build Facebook and Instagram. [PayPal, KhanAcademy, and Flipkart](https://github.com/reactjs/redux/issues/310) use Redux for state management. +它们并不孤单:Angular、React、Redux 和 Lodash 是 JavaScript 生态系统中主要的框架和库,同时它们都被函数式编程很深的影响到 — 或者在 Lodash 和 Redux中,明确的表达是为了在实际的 JavaScript 应用中使用函数式编程模式。 -They’re not alone: Angular, React, Redux, and Lodash are the leading frameworks and libraries in the JavaScript application ecosystem, and all of them are heavily influenced by functional programming — or in the cases of Lodash and Redux, built for the express purpose of enabling functional programming patterns in real JavaScript applications. +“为什么是 JavaScript?”因为实际上所有的软件呢公司用来开发软件的语言。无论如何,JavaScript 从 Lisp 这个数十年来的标志头上偷取了 “最受欢迎的函数式编程语言” 的头衔。事实上,Haskell 更适合当今函数式编程概念的标准,但是人们并不使用它来开发实际应用。 -“Why JavaScript?” Because JavaScript is the language that most real companies are using to build real software. Love it or hate it, JavaScript has stolen the title of “most popular functional programming language” from Lisp, which was the standard bearer for decades. True, Haskell is a much more suitable standard bearer for functional programming concepts today, but people just aren’t building as many real applications in Haskell. +在任何时候,在美国都有一百万的 JavaScript 工作需求,世界其他地方也有数百万的量。学习 Haskell 可以帮助你很好的学习函数式编程,但学习 JavaScript 将会教会你在实际工作中开发应用。 -At any given moment, there are close to a hundred thousand JavaScript job openings in the United States, and hundreds of thousands more world-wide. Learning Haskell will teach you a lot about functional programming, but learning JavaScript will teach you a lot about building production apps for real jobs. +App 正在吞食世界, web 正在吞食 app, 同时 JavaScript 正在吞食 web。 -Apps ate the world, the web ate apps, and JavaScript ate the web. +[**接下来的第三部分: 函数式开发者的 JavScript 介绍…**](https://medium.com/javascript-scene/a-functional-programmers-introduction-to-javascript-composing-software-d670d14ede30#.zdpw16p65) -[**Continued in Part 3: A Functional Programmer’s Introduction to JavaScript…**](https://medium.com/javascript-scene/a-functional-programmers-introduction-to-javascript-composing-software-d670d14ede30#.zdpw16p65) +### 下一步 -### Next Steps +想更多的学习 JavaScript 的函数式编程? -Want to learn more about functional programming in JavaScript? +[Learn JavaScript with Eric Elliott](http://ericelliottjs.com/product/lifetime-access-pass/),什么,你还不是其中之一,out 了! -[Learn JavaScript with Eric Elliott](http://ericelliottjs.com/product/lifetime-access-pass/). If you’re not a member, you’re missing out! +[![](https://cdn-images-1.medium.com/freeze/max/30/1*3njisYUeHOdyLCGZ8czt_w.jpeg?q=20)![](https://cdn-images-1.medium.com/max/800/1*3njisYUeHOdyLCGZ8czt_w.jpeg)](https://ericelliottjs.com/product/lifetime-access-pass/) -[ -](https://ericelliottjs.com/product/lifetime-access-pass/) +*Eric Elliott* 是 [*“Programming JavaScript Applications”*](http://pjabook.com) (O’Reilly) 和 “Learn JavaScript with Eric Elliott” 的作者。他曾效力于 *Adobe Systems, Zumba Fitness, he Wall Street Journal, ESPN, BBC, and top recording artists including Usher, Frank Ocean, Metallica* 和其他一些公司。 -***Eric Elliott*** is the author of [*“Programming JavaScript Applications”*](http://pjabook.com) (O’Reilly), and [*“Learn JavaScript with Eric Elliott”*](http://ericelliottjs.com/product/lifetime-access-pass/). He has contributed to software experiences for **Adobe Systems, Zumba Fitness, The Wall Street Journal, ESPN, BBC, and top recording artists including Usher, Frank Ocean**, Metallica, and many more. - -*He spends most of his time in the San Francisco Bay Area with the most beautiful woman in the world.* +*他和她的老婆(很漂亮)大部分时间都在旧金山湾区里。* --- From b029545ce7db113488a20a0722eb40027cff9f44 Mon Sep 17 00:00:00 2001 From: sunxinlei Date: Sun, 9 Apr 2017 18:41:57 +0800 Subject: [PATCH 091/638] =?UTF-8?q?=E6=A0=B9=E6=8D=AE=20sqrthree=20?= =?UTF-8?q?=E6=A0=A1=E5=AF=B9=E6=84=8F=E8=A7=81=E6=9B=B4=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO/modern-javascript-for-ancient-web-developers.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/TODO/modern-javascript-for-ancient-web-developers.md b/TODO/modern-javascript-for-ancient-web-developers.md index d62d526e6a9..f4cbf50cb77 100644 --- a/TODO/modern-javascript-for-ancient-web-developers.md +++ b/TODO/modern-javascript-for-ancient-web-developers.md @@ -26,13 +26,13 @@ Hi.我是一个正在学习现代 JavaScript 的“老派” web 开发者。我 这种情况下,就有必要向你身边的 JavaScript 工程师朋友伸手求助了,和他们聊一聊你的技术路线。我很荣幸在 Postlight 得到了工程师朋友(特别是 [Jeremy Mack](https://medium.com/@mutewinter))的精湛指导,感谢他们容忍我无穷无尽的问题。 -我要说的是,学习现代的 JavaScript 需要人为干预。事物还在不断发展变化,各种教程尚未成熟和定型,所谓最佳实践也未形成正式规范。如果你身边没有大牛,那么至少也得检查 Medium 上文章或教程的日期,或 Github 仓库的最近一次提交时间。如果时间超过了一年,基本上可以确定已经过时。 +我要说的是,学习现代的 JavaScript 需要人为干预。事物还在不断发展变化,各种教程尚未成熟和定型,所谓最佳实践也未形成正式规范。如果你身边没有大牛,那么至少也得检查 Medium 上文章或教程的日期,或 GitHub 仓库的最近一次提交时间。如果时间超过了一年,基本上可以确定已经过时。 ### 新的问题,而不是已经确定的解决方案 ### 走类似这样的路线:当你在学习现代 JavaScript 时,你遇到的问题的解决方案还在渐渐得到解决,这正是一个好机会。事实上,很可能仅仅差一次 code review,你在使用这个包时就可以修复问题。 -当你在使用一种像 PHP 这样的古老的语言的时候,你可以 Google 一个提问或者问题,几乎百分之百能找到一个5年前的 Stack Overflow 回答来解决它,或者你能在(详尽的,大量评论的,无与伦比的)[文档](http://docs.php.net/docs.php)里找到整个描述。 +当你在使用一种像 PHP 这样的古老的语言的时候,你可以 Google 一个提问或者问题,几乎百分之百能找到一个 5 年前的 Stack Overflow 回答来解决它,或者你能在(详尽的、大量评论的、无与伦比的)[文档](http://docs.php.net/docs.php)里找到整个描述。 现代 JavaScript 就并非如此了。 我曾经徜徉在 GitHub issues 和源码的时候不止一次找到的都是一些过时的文档。剖析 GitHub 版本库是学习和使用各种包的一部分,而且对于我这样的“老派人”,差之毫厘的学习总是令人迷惑。 @@ -42,7 +42,7 @@ Hi.我是一个正在学习现代 JavaScript 的“老派” web 开发者。我 [![Markdown](http://i4.buimg.com/1949/adafb30475d3d36a.png)](https://twitter.com/capndesign/status/832638513048850433/photo/1) -**不要因此止步不前**。我不得不放手去做,从起步到正确配置,允许自己的不完美甚至一些业余,只为舒适地使用自己的工具。(我不会告诉你我曾用[nodemon](https://nodemon.io/)做代码检查)随后我会找到更好的方法并且在每个新项目中纳入进来。 +**不要因此止步不前**。我不得不放手去做,从起步到正确配置,允许自己的不完美甚至一些业余,只为舒适地使用自己的工具。(我不会告诉你我曾用 [nodemon](https://nodemon.io/) 做代码检查)随后我会找到更好的方法并且在每个新项目中纳入进来。 这方面 JS 还有大量的工作要做。现代 JavaScript 领域依然是不断变化的,但我一个现代 JS 工程师亲友告诉我,[这份来自 Jonathan Verrecchia 的教程](https://github.com/verekia/js-stack-from-scratch)是目前构建一个当代 JavaScript 栈的不二之选。对,就是现在。 @@ -54,17 +54,17 @@ Hi.我是一个正在学习现代 JavaScript 的“老派” web 开发者。我 这是迄今为止我在这个过程中经历过的一些研讨会和教程的不完整列表。 -- [HOW-TO-NPM](https://github.com/workshopper/how-to-npm) —— npm 是 JavaScript 的包管理器。即使在学习这个教程之前我已经敲打过上千次 “npm install”,但是知道学完这个我才知道 npm 做的所有事情。(在很多项目中我已经转移使用[yarn](https://github.com/yarnpkg/yarn),而不是 npm,但所有的概念都是相通的) +- [HOW-TO-NPM](https://github.com/workshopper/how-to-npm) —— npm 是 JavaScript 的包管理器。即使在学习这个教程之前我已经敲打过上千次 “npm install”,但是知道学完这个我才知道 npm 做的所有事情。(在很多项目中我已经转移使用[yarn](https://github.com/yarnpkg/yarn),而不是 npm,但所有的概念都是相通的) `npm i -g how-to-npm` -- [learnyounode](https://github.com/workshopper/learnyounode)——我打算专注于服务端 JavaScript,因为那有令我安逸的东西,那就是 Node.js。Learnyounode 是一个交互式教程,结构上类似 how-to-npm。 +- [learnyounode](https://github.com/workshopper/learnyounode)——我打算专注于服务端 JavaScript,因为那有令我安逸的东西,那就是 Node.js。Learnyounode 是一个交互式教程,结构上类似 how-to-npm。 - [expressworks](https://github.com/azat-co/expressworks) —— 和前面两个项目类似,Expressworks 是 Express.js 的介绍,一个 Node.js 的 web 框架。在 Postlight 公司 Express 没有得到广泛使用,但对于初学者,它值得学习去上手构建一个简单的 web 应用。 - 现在是时候做点真东西了。我发现 Tomomi Imura 的一篇教程 [Creating a Slack Command Bot from Scratch with Node.js](http://www.girliemac.com/blog/2016/10/24/slack-command-bot-nodejs/) 已经可以学到足够的 Node 和 Express 的新技能来应对工作。因为我专注于后端,使用 Slack 创建一个 “/” 命令是一个很好的开始,因为没有前端演示(Slack 帮你做好了) -- 在构建这个命令的过程中,我不使用演练中所推荐的 ngrok 或者 Heroku,而是使用 [Zeit Now](https://zeit.co/now),这是任何人可用的,创建快速一次性的 JS 应用的宝贵工具。 +- 在构建这个命令的过程中,我不使用演练中所推荐的 ngrok 或者 Heroku,而是使用 [Zeit Now](https://zeit.co/now),这是任何人可用的、创建快速一次性的 JS 应用的宝贵工具。 - 一旦开始写真正意义的代码,我也开始掉下工具无底洞了,安装 Sublime 插件,获取正确的 [Node 版本](https://github.com/postlight/lux/blob/master/CONTRIBUTING.md#nodejs-version-requirements),配置 ESLint,使用 [Airbnb 的代码规范 (Postlight 公司的偏好)](https://github.com/airbnb/javascript) —— 这些事情拖了我的后退,但也都是有价值的初始化投资。对于这方面我还在坑里,例如 Webpack 对我来说依然美妙又神秘,不过[这个视频是个很不错的介绍](https://www.youtube.com/watch?v=WQue1AN93YU)*.* - 某些时候 JS 的异步执行(特别是[回调地狱](http://callbackhell.com/))开始困扰我,[Promise It Won’t Hurt](https://github.com/stevekane/promise-it-wont-hurt) 是另一个教你怎样使用 Promise 书写优雅异步逻辑的教程。Promise 是用于解决异步执行的 JS 新概念。说实话 Promise 令我耳目一新,他们是巧妙的范式转变。感谢 [Mariko Kosaka](http://kosamari.com/notes/the-promise-of-a-burger-party),现在我每次买汉堡的时候都能想起这些。 From be832cc766613663cdf57b56822961647907101b Mon Sep 17 00:00:00 2001 From: sunxinlei Date: Sun, 9 Apr 2017 18:47:15 +0800 Subject: [PATCH 092/638] =?UTF-8?q?=E4=B8=A4=E5=A4=84=E8=8B=B1=E6=96=87?= =?UTF-8?q?=E7=A9=BA=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO/modern-javascript-for-ancient-web-developers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TODO/modern-javascript-for-ancient-web-developers.md b/TODO/modern-javascript-for-ancient-web-developers.md index f4cbf50cb77..844e8f62cdf 100644 --- a/TODO/modern-javascript-for-ancient-web-developers.md +++ b/TODO/modern-javascript-for-ancient-web-developers.md @@ -22,7 +22,7 @@ Hi.我是一个正在学习现代 JavaScript 的“老派” web 开发者。我 ### 转移目标 (.jS) -现代 JS 的特点就是朝气蓬勃和发展迅速,所以很容易就选择了过时的框架、模板引擎、构建工具、 教程或者已经不是最佳实践的技术。( 如果真有一个被广泛接受的最佳实践的概念的话) +现代 JS 的特点就是朝气蓬勃和发展迅速,所以很容易就选择了过时的框架、模板引擎、构建工具、 教程或者已经不是最佳实践的技术。(如果真有一个被广泛接受的最佳实践的概念的话) 这种情况下,就有必要向你身边的 JavaScript 工程师朋友伸手求助了,和他们聊一聊你的技术路线。我很荣幸在 Postlight 得到了工程师朋友(特别是 [Jeremy Mack](https://medium.com/@mutewinter))的精湛指导,感谢他们容忍我无穷无尽的问题。 @@ -54,7 +54,7 @@ Hi.我是一个正在学习现代 JavaScript 的“老派” web 开发者。我 这是迄今为止我在这个过程中经历过的一些研讨会和教程的不完整列表。 -- [HOW-TO-NPM](https://github.com/workshopper/how-to-npm) —— npm 是 JavaScript 的包管理器。即使在学习这个教程之前我已经敲打过上千次 “npm install”,但是知道学完这个我才知道 npm 做的所有事情。(在很多项目中我已经转移使用[yarn](https://github.com/yarnpkg/yarn),而不是 npm,但所有的概念都是相通的) +- [HOW-TO-NPM](https://github.com/workshopper/how-to-npm) —— npm 是 JavaScript 的包管理器。即使在学习这个教程之前我已经敲打过上千次 “npm install”,但是知道学完这个我才知道 npm 做的所有事情。(在很多项目中我已经转移使用 [yarn](https://github.com/yarnpkg/yarn),而不是 npm,但所有的概念都是相通的) From 3fc96b46eccefccbe0c2f147247c63484c942f48 Mon Sep 17 00:00:00 2001 From: lsvih Date: Sun, 9 Apr 2017 20:51:55 +0800 Subject: [PATCH 093/638] =?UTF-8?q?=E4=BA=8C=E6=A0=A1=E6=84=8F=E8=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO/modules-vs-microservices.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/TODO/modules-vs-microservices.md b/TODO/modules-vs-microservices.md index 743f181583e..fcac5d43e3b 100644 --- a/TODO/modules-vs-microservices.md +++ b/TODO/modules-vs-microservices.md @@ -6,11 +6,11 @@ # 模块化 vs. 微服务 -在避免微服务复杂性的同时,应用模块化系统设计设计原则。 +使用模块化系统设计原则来避免微服务的复杂性。 ![](https://d3tdunqjn7n0wj.cloudfront.net/360x240/container-227877_1920-0db52b796e6b80d98f6df2d01a6ee4fb.jpg) -从单体式应用向微服务架构迁移已经是老生常谈的话题了。除了过过嘴瘾,似乎真的动手将单体式应用拆分成微服务也不是什么很困难的事。但是这种做法真的是你们团队的最佳选择吗?维护一个凌乱的单体式应用的确很伤脑筋,但是还有另一种强有力但常常被人忽视的替代方案:模块化应用开发。本文将探讨这种替代方案,并展现其与构建微服务的关系。 +从单体式应用向微服务架构迁移已经是老生常谈的话题了。除了过过嘴瘾,似乎真的动手将单体式应用拆分成微服务也不是什么很困难的事。但是这种做法真的是你们团队的最佳选择吗?维护一个凌乱的单体式应用的确很伤脑筋,但是还有另一种优秀但常常被人忽视的替代方案:模块化应用开发。本文将探讨这种替代方案,并展现其与构建微服务的关系。 ## 模块化微服务 @@ -40,9 +40,9 @@ 例如在 Java 中,有几个可以帮助你构建应用的模块系统。OSGi 是其中最著名的一个,不过随着 Java 9 的发布,Java 平台将加入一个原生的模块系统。现在模块作为一等结构(first-class construct),成为了语言和平台的一部分。Java 模块可以表明对其它模块的依赖,以及在强封装实现类的时候公开暴露接口。甚至 Java 平台本身(一个庞大的代码库)已经使用了新的 Java 模块系统进行模块化。你可以在我即将出版的书[Java 9 Modularity](https://www.safaribooksonline.com/library/view/java-9-modularity/9781491954157/?utm_source=newsite&utm_medium=content&utm_campaign=lgen&utm_content=modules-vs-microservices-inline)中了解有关 Java 9 模块化开发的更多信息。(现早期版本已经发布) -其它的语言也提供了类似的机制。例如,JavaScript 在 ES2015 规范中提供了一个[模块系统](http://exploringjs.com/es6/ch_modules.html)。在此之前,Node.js 也为 JavaScript 后端提供了一个非标准的模块系统。然而 JavaScript 作为一种动态语言,对于强制接口(类型)与模块封装的支持还是较弱。你可以考虑在 JavaScript 的基础上使用 TypeScript 来重新获得这些优点。微软的 .Net 框架与 Java 一样都有着强类型,但就强封装以及程序集(Assemblies)间的显式依赖而言,它与 Java 即将推出的模块系统并不相同。尽管如此,你可以通过使用 [.Net Core](https://msdn.microsoft.com/en-us/magazine/mt707534.aspx) 中标准化的反转控制模式(IOC)以及创建逻辑相关的程序集来实现良好的模块化架构。即使是 C++ 也在以后的版本中[考虑添加](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/n4610.pdf)模块系统。许多语言现在都对模块化表现出了欣赏之情,这本身就是一个显著的进步。 +其它的语言也提供了类似的机制。例如,JavaScript 在 ES2015 规范中提供了一个[模块系统](http://exploringjs.com/es6/ch_modules.html)。在此之前,Node.js 也为 JavaScript 后端提供了一个非标准的模块系统。然而 JavaScript 作为一种动态语言,对于强制接口(类型)与模块封装的支持还是较弱。你可以考虑在 JavaScript 的基础上使用 TypeScript 来重新获得这些优点。微软的 .Net 框架与 Java 一样都有着强类型,但就强封装以及程序集(Assemblies)间的显式依赖而言,它与 Java 即将推出的模块系统并不相同。尽管如此,你可以通过使用 [.Net Core](https://msdn.microsoft.com/en-us/magazine/mt707534.aspx) 中标准化的反转控制模式(IOC)以及创建逻辑相关的程序集来实现良好的模块化架构。即使是 C++ 也在以后的版本中[考虑添加](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/n4610.pdf)模块系统。许多语言都在向模块化靠近,这本身就是一个显著的进步。 -当你有意识地使用你的开发平台的模块化特性时,你就可以实现之前提及的微服务的模块化优势。基本上模块系统越好,你在开发过程中获得的帮助就越多。只要在不同团队间的接触点定义好明确的接口,不同的团队也可以独立进行不同部分的工作。当然,在部署时还是要将模块在一个单独的部署单元中组合起来。这样可以防止过大的复杂度,以及迁移到微服务所需要的开发与管理成本。诚然,这也意味着你不能使用不同的技术栈来构建不同的模块,但你的团队应该不会真的这么做吧? +当你有意识地使用你的开发平台的模块化特性时,你就可以实现之前提及的微服务的模块化优势。基本上模块系统越好,你在开发过程中获得的帮助就越多。只要在不同团队间的接触点定义好明确的接口,不同的团队也可以独立进行不同部分的工作。当然,在部署时还是要将模块在一个单独的部署单元中组合起来。这样可以防止过于复杂,以及减少迁移到微服务所需要的开发与管理成本。诚然,这也意味着你不能使用不同的技术栈来构建不同的模块,但你的团队应该不会真的这么做吧? ## 模块设计 @@ -68,7 +68,7 @@ ## 总结 -总之,最好的方案就是找到一个折中的点。这两种方案都有可取之处,需要根据实际环境、组织和应用本身进行选择。既然你可以在之后迁移成微服务架构,那为什么最开始不直接使用模块化应用呢?如果你之前就已经划分好了模块边界,那也就不需要再去拆分你的单体式应用了。甚至你还可以在模块内部搭建微服务架构。那么问题就变成了:为什么微服务一定要是”微“的呢? +总之,最好的方案就是找到一个折中的点。这两种方案都有可取之处,需要根据实际环境、组织和应用本身进行选择。既然你可以在之后迁移成微服务架构,那为什么最开始不直接使用模块化应用呢?如果你之前就已经划分好了模块边界,那也就不需要再去拆分你的单体式应用了。甚至你还可以在模块内部搭建微服务架构。那么问题就变成了:为什么微服务一定要是“微”的呢? 即使你的应用刚从模块化应用转成微服务架构,服务也不必非得很“微”才具备可维护性。在服务中应用模块化原则能让它们在复杂度的可扩展性上超越通常的微服务。现在这份蓝图中既有微服务也有模块,减少架构中的服务的数量可以节约成本;而其中的模块可以像构建单体式应用一样,构建和扩展服务。 From b20456939ffc66de053d0857be6fd3294e961dcc Mon Sep 17 00:00:00 2001 From: ivyxuan Date: Sun, 9 Apr 2017 20:59:48 +0800 Subject: [PATCH 094/638] =?UTF-8?q?=E6=A0=B9=E6=8D=AE=20ylq167=20=E5=92=8C?= =?UTF-8?q?=20gaozp=20=E7=9A=84=E6=84=8F=E8=A7=81=E8=BF=9B=E8=A1=8C?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO/the-details-that-matter.md | 90 ++++++--------------------------- 1 file changed, 15 insertions(+), 75 deletions(-) diff --git a/TODO/the-details-that-matter.md b/TODO/the-details-that-matter.md index 401d203b9d2..fce6047f6df 100644 --- a/TODO/the-details-that-matter.md +++ b/TODO/the-details-that-matter.md @@ -1,215 +1,155 @@ > * 原文地址:[The Details That Matter](https://uxplanet.org/the-details-that-matter-8b962ca58b49#.ypigeruoq) > * 原文作者:[Nick Babich](https://uxplanet.org/@101?source=post_header_lockup) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 译者: -> * 校对者: +> * 译者:[ivyxuan](https://github.com/iloveivyxuan) +> * 校对者:[ylq167](http://www.11167.xyz)、[gaozp](https://github.com/gaozp) -# The Details That Matter # # 细节是产品设计的重中之重 # -Your product’s success is based on a combination of factors, but the overall user experience tops them all. When it comes down to designing a new app or a site, sticking to best practices is a solid way to go, but during the creation of the big picture, it’s fairly easy to skimp over design elements that feel like nice to have but not necessary. However, the difference between good and bad experiences often comes down to how thoughtful we can design these small details. 一个产品的成功是由各种因素共同造就的,而其中最重要的因素,就是整体的用户体验。在设计一款新的应用或是网站的时候,坚持最佳的实践规范是一个可靠的方法,但是在创造宏伟蓝图的时候,人们很容易就会省略掉那些能让人有更好的体验但却并非必要的设计元素。然而,设计的优劣往往在于我们能设计出多么体贴的细节。 -In this article, I’ll focus on *visual feedback*, *microcopy* and *whitespace* and you’ll see why these *little big details* are just as important as the more obvious elements of your design, and how they help determine the success of your product. -在这篇文章中,我将会关注可视化反馈、小的提示信息还有留白这几个方面,你会发现为什么这些不起眼的细节和那些显眼的设计元素相比同样重要,而这些细节又是怎样决定你产品成败的。 +在这篇文章中,我将会重点关注**可视化反馈**、**小的文字信息**还有**留白**这几个方面,你将会发现为什么这些不起眼的细节和那些显眼的设计元素相比同样重要,而这些细节又是怎样决定你产品成败的。 -### Visual Feedback ### ### 可视化反馈 ### -Visual feedback might be easily overlooked in the greater design scheme, but it actually hold the entire experience together. When there’s no feedback there’s no proper interaction. Imagine talking to someone who doesn’t respond in any way — you can’t communicate at all. Same goes for your app. 可视化反馈在较大的设计方案里很容易就会被忽视掉,但它实际上贯穿整体的用户体验流程。可以说,如果没有反馈就没有所谓的交互,你能想象和一个人聊天,可他一点反应也不给你吗 —— 你根本就聊不下去。而对于你的应用也是同样的道理。 -> *Lack of visual feedback is able to confuse users.* > **缺乏可视化的反馈会让用户感到困惑。** -You must ensure that there is always some feedback for user actions, because it makes users feel in control. Visual feedback -你必须要确保对于用户的每个动作都有相应的反应,因为这会让用户会感觉他在控制着一切。可视化反馈 +你必须要确保对于用户的每个动作都有相应的反馈,因为这会让用户感觉应用运行一切正常。可视化反馈 -- Acknowledges that the app has received a user’s action. -- Communicates the results of interaction, making it both *visible* and *understandable*. Gives the user a signal that they (or the app) have succeeded or failed at performing a task. -- 首先表明这个应用了解了用户的操作。 +- 首先表明这个应用接受到了用户的操作。 - 然后它通过一种可视化而且易于理解的方式告诉用户这次交互的结果是什么,通过给用户一个信号,来告诉用户自己对于这个任务的执行是成功还是失败。 -#### Make buttons and other controls tangible #### -#### 让按钮或是其他开关看起来是可以碰的 #### +#### 让按钮或是其他开关看起来是可触摸的 #### -In real life, buttons, controls and objects respond to our interaction, and this is how people expect things to work. People expect a similar level of responsiveness from app elements. 在现实生活中,按钮、开关还有其他东西都会对我们的动作有所回应,人们觉得世界就是这样运转的。而同样,人们也会期待应用里的元素能有类似的回应。 -Image credits: [Ramotion](https://dribbble.com/shots/1749645-Contact-Sync) 图片来源:[Ramotion](https://dribbble.com/shots/1749645-Contact-Sync) -#### Result of operation #### #### 操作的结果 #### -Visual feedback is also helpful when you need to inform users about results of an operation. You can use existing elements to deliver a feedback. -当你需要告诉用户他的操作结果是什么的时候,可视化反馈就很有用了。你可以利用现有的元素去传递反馈信息。 +当你需要告诉用户他的操作结果是什么的时候,可视化反馈就很有用了,你可以利用现有的元素去传递反馈信息。 -Image credits: [Colin Garven](https://dribbble.com/ColinGarven) 图片来源:[Colin Garven](https://dribbble.com/ColinGarven) -#### **System should tell users its state** #### -#### **系统需要告诉用户他的状态是什么** #### +#### **系统需要告诉用户他当前的状态是什么** #### -Users want to know their current context in a system at any given time and apps shouldn’t keep them guessing — they should tell the user what’s happening via appropriate visual feedback. For frequent and minor actions, the response can be modest, while for infrequent and major actions, the response should be more substantial. -在系统中,用户任何时候都会想知道他此刻的状态是什么,而这不应该让用户自己去猜 —— 所以系统应该通过恰当的可视化反馈告诉用户此刻正在发生什么。对于一些常见而且次要的操作,适当的反馈就可以了,而对于不寻常而且重要的操作,反馈就需要更明显一点。 +在系统中,用户任何时候都会想知道他此刻的状态是什么,而这不应该让用户自己去猜 —— 所以系统应该通过恰当的可视化反馈告诉用户此刻正在发生什么。对于一些常见而且次要的操作,简单的反馈就可以了,而对于不寻常而且重要的操作,反馈就需要更明显一点。 -- [Animated notification](https://uxplanet.org/3-key-uses-for-animation-in-mobile-ui-design-4d7c482dd84b#.x07lyyazb) makes it possible for a user to quickly understand the current status. - [动态提醒](https://uxplanet.org/3-key-uses-for-animation-in-mobile-ui-design-4d7c482dd84b#.x07lyyazb)可以让用户立刻明白此时的状态。 -Image credits: [Eddy Gann](https://dribbble.com/SMSeddy) 图片来源:[Eddy Gann](https://dribbble.com/SMSeddy) -- [Loading animations](https://uxplanet.org/progress-indicators-in-mobile-ux-design-a141e22f3ea0#.etoavwmbw) provides real-time notification of app’s process status, enabling the user to quickly understand what is going on. - [加载动画](https://uxplanet.org/progress-indicators-in-mobile-ux-design-a141e22f3ea0#.etoavwmbw)是对于应用进程的实时提醒,可以让用户立刻明白现在加载到哪里了。 -A loading bar engages the user and prevents confusion when app is busy loading data. Image credits: [Mark](https://dribbble.com/milkycookie) -图片来源:[Mark](https://dribbble.com/milkycookie) -应用无法快速加载信息的时候,进度条可以避免让用户感到困惑。图片来源:[Mark](https://dribbble.com/milkycookie) +应用加载信息的时候,进度条可以避免让用户感到困惑。图片来源:[Mark](https://dribbble.com/milkycookie) -### Microcopy ### ### 少量的文字信息 ### -Microcopy is the little bits of text that guide users through an experience. Microcopy examples are error messages, button labels, hint text. At a glance, these tiny clusters of words seem insignificant when compared to the overall app design. But surprisingly, they have a huge impact on conversions. 少量的文字信息是一些用来是指导用户行为的一点点文字。举一些例子就是,错误信息、按钮对应的标签、提示信息。乍看之下,这么少的文字和整个应用设计比起来一点也不重要,但出人意料的是,它们对转化率有着极为重要的影响。 -> Writing good microcopy in your app is just as important as having the app work correctly and the user interface being easy and efficient to use. > 在应用里写出好的文字信息,和让应用正常运行、用户界面易于使用一样重要。 -#### Show that you’re human #### #### 让应用看起来像一个有血有肉的人 #### -A quick way to make your UI warmer and less mechanical is a human tone in the copy. If your product sounds human, it’s easier for people to trust you. 有一个快速的方法能让你的 UI 变得温暖而不呆板,就是用人说话的口吻去描述内容。如果你的产品听起来好像是一个人,用户就会更加的信任你。 -Yelp shows they have real humans in charge. Yelp 表现得好像他们是真人在负责这件事情。 -Airbnb sound human and conversational Airbnb 的提示听起来像人说的话而且语气还很随和。 -#### Use friendly and helpful copy in a moment of failure #### #### 报错的方式要友好而且有效 #### -How errors are communicated can have a huge impact on the way someone experiences your product. Often overlooked, an ill-constructed error message can fill users with frustration. -表达错误信息的方式会严重影响产品的用户体验。通常来说,省略错误信息或是没有正确描述错误信息都会让用户感到失望。 +表达错误信息的方式会严重影响产品的用户体验。通常来说,省略错误信息或是没有正确描述错误信息都会让用户受挫。 -*An alert message such as “An error occurred” is mystifying to all users and is likely to annoy experienced users.* **像“出错啦”这种警告对所有的用户都会造成困扰,而且还会惹恼专家级用户。** -A well-crafted error message, on the other hand, can turn a moment of frustration into a moment of delight. Thus, make error messages human, not technical, and suited to your audience. 但是,一个精心设计过的错误信息,会顿时化失望为欣喜。所以,把报错变得人性化、不用技术性的语言并且适合你的用户群体。 -Error states must incorporate concise, friendly, and instructive copy as to what to do next. 错误状态一定要具体、友好而且有用,要告诉用户下一步怎么做。 -#### Alleviate users concerns #### #### 减少用户的担忧 #### -Microcopy is extremely contextual. That’s why it’s so valuable. It answers a very specific question people have and speaks to their concerns right on the spot. For example, microcopy can be fundamental in reassuring your users at the point of subscribing or sharing details. Whilst ‘not to spam/auto-tweet’ might be taken for granted by good marketers when asking for email address/access to the social network account connections, the user is less than sure. Thus, when people add their emails or connect their Twitter accounts, say “we hate spam as much as you do.” 这些少量的文字信息是很情景化的,这也是为什么它很重要的一个原因。它可以解答用户具体情况下的问题,并针对他们所担忧的事情进行直接地交流。举例来说,当用户选择订阅或是提供了具体信息的时候,一些文字信息对于消除用户担忧会起到相当关键的作用。对于优秀的营销人员来说,“不会有群发消息或是自动关注”是理所当然的事情,但用户自己会存疑。因此,当用户添加了他们的邮箱地址或者绑定了 Twitter 账号的时候,一定要明确表态“我们和你一样讨厌垃圾消息”。 -This microcopy covers all the potential user concerns in one tight little sentence. 这只是一小段紧凑的文字,却涵盖了用户所有潜在的担忧。 -### Whitespace ### ### 留白 ### -Whitespace (or negative space) is the areas of a design where there is no element placed by a designer. Elements of whitespace are space around images, margins, paddings, line-spacing and letter-spacing within text content. Although many may consider it a waste of valuable screen estate, whitespace is an essential element in user interface design. -留白(或者说是负空间)是设计师没有摆放设计元素的地方。而设计元素间的留白是指处在图片间、内填充(margin)、外填充(padding)和行间、字间的空白。虽然很多人觉得这些空白浪费了宝贵的界面位置,但其实,留白是用户界面设计的一个重要元素。 +留白(或者说是负空间)是设计师没有摆放设计元素的地方。而设计元素间的留白是指处在图片间距、内边距、外边距、行间距和字间距的空白。虽然很多人觉得这些空白浪费了宝贵的界面位置,但其实,留白是用户界面设计的一个重要元素。 -#### Improve UI comprehension #### #### 让用户界面更容易理解 #### -*Clutter is bad.* Cluttering your interface overloads your user with too much information: every added button, image, and line of text make the screen more complicated. If you don’t think any part of your design should be intentionally blank, take a look at example below and you’ll see what happens when too many objects competing for your attention. **杂乱的堆砌是很糟糕的一件事情。**在界面上杂乱堆砌元素会给用户带来过多的信息:每一个被添加的按钮、图片和文字都会让界面显得更加复杂。如果你不想你的设计有任何刻意的留白的话,下面这个例子就能很明白的告诉你,有太多东西一起吸引你的注意力是多么可怕的事情。 -A cluttered UI is unattractive and doesn’t make users want to scan it, especially when there’s no visual hierarchy within the view. -杂乱堆砌的 UI 尤其是没有视觉层次的 UI 会让用户没有拥有一点想要审视的欲望。 +杂乱堆砌的 UI 尤其是没有视觉层次的 UI 会让用户没有一点想要审视的欲望。 -The power of white space comes from the limits of human attention and memory. Our short-term memory can hold a small amount of information (typically [around 7 items or even less](http://www.human-memory.net/types_short.html)) in mind in an active, readily-available state for a short period of time (typically from 10 to 15 seconds, or sometimes up to a minute). -留白之所以很重要,是因为用户的注意力还有记忆里是有限的。我们的短期记忆只可以在短时间内(通常来说是 10 到 15 秒钟、或者是 1 分钟以内)记住一点有效的既有信息(通常来说[是 7 个事物或者比这个更少](http://www.human-memory.net/types_short.html))。 +留白之所以很重要,是因为用户的注意力还有记忆是很有限的。我们的短期记忆只可以在短时间内(通常来说是 10 到 15 秒钟、或者是 1 分钟以内)记住一点有效的既有信息(通常来说[是 7 个事物或者比这个更少](http://www.human-memory.net/types_short.html))。 -> User attention is a precious resource, and should be allocated accordingly. > 用户的注意力是很珍贵的资源,所以必须合理的分配。 -If cluttering your interface overloads your user with too much information, then reducing the clutter will improve comprehension. Generous whitespace can make some of the messiest interfaces look inviting and simple. Whitespace reduces the amount of elements users see all at once and makes scanning much easier. The skill of using whitespace lies in providing your users with a digestible amount of content ([chunks of content](https://uxplanet.org/best-practices-for-cards-fa45e3ad94dd#.by8pzk56q)), then stripping away extraneous details. 如果因为你界面上的胡乱堆砌使得用户接收过多的信息,那么减少一些杂物就能改善用户的理解。大方地使用留白可以让凌乱的界面变得简单而有吸引力,留白削减了用户乍看之下接收到的元素数量,这使得浏览信息变得更加容易。留白的使用技巧在于只给用户提供能让他消化的数量的内容([一定数量的内容](https://uxplanet.org/best-practices-for-cards-fa45e3ad94dd#.by8pzk56q)),然后去掉不必要的细节。 -Medium is a great example of using whitespace to improve content and UI comprehension. Medium 是一个典型的例子,它善于使用留白来改善用户对内容和 UI 的理解。 -#### Draw attention to elements #### #### 让元素更具有吸引力 #### -Whitespace creates the spaces around elements in the design to help them stand out or separate from the other elements. It helps communicate what’s most important and needs attention. 留白在设计中是通过在元素周围留出空白,以让元素更加突出或是和其他元素以进行区分。它可以告诉用户什么是最重要而且是需要格外注意的。 -> The more space you provide around the element, the more focused it becomes. > 元素周围的留白越多,元素就会越引人注目。 -Google Search homepage is a great example of using whitespace. The layout immediately facilitates the user goal by placing primary interaction element (search box) in front and center, with plenty of white space on either side to add emphasis. 谷歌搜索的首页就是一个使用留白的典型例子,它通过在正中央摆放其最重要的交互元素(搜索框),并且在周围留出足够的空白以凸显其重要性的布局,直接实现了用户目标。 -The lack of other elements will make existing elements stand out more. 去掉其他的元素可以更加凸显留下来的元素。 -#### Clarifying relationships #### #### 明确关系 #### -The [law of proximity](https://www.interaction-design.org/literature/article/laws-of-proximity-uniform-connectedness-and-continuation-gestalt-principles-2) describes how the human eye perceives connections between visual elements. It states that objects near to each other appear similar. We can use whitespace to build perceived relationships between different elements. Take a look at this picture. Almost everyone sees two groups of dots, rather than simply 16 dots. [接近法则](https://www.interaction-design.org/literature/article/laws-of-proximity-uniform-connectedness-and-continuation-gestalt-principles-2)描述了人的眼睛是如何划分视觉元素的,它阐述说距离更近的物体看起来更相似。我们可以利用留白,在不同的元素间产生视觉联系。你看下面这幅图片,几乎所有人都会说看到了两组点,而不是16个点。 -Breaking the information up into appropriate groups can help make it feel more scannable and readable. In the form on the right, categorising the 15 fields into three groups makes the process feel easier. The amount of content is the same, but the impression on users is much different. 将信息拆分成合适的组别可以让信息更好的被识别和阅读。右边的表单将 15 栏拆分成 3 组,这样填写表单也变得相对容易了。虽然内容的数量仍然相同,但是给用户的感觉却完全不同。 -Image credits: NNGroup 图片来源:NNGroup -### Conclusion ### ### 总结 ### -*Design with care.* Each minor detail in your app’s UI deserves close attention because UX is the sum of all details working harmoniously: **用心设计,应用界面上的每一个小的细节都值得细心揣摩,因为用户体验就是由这些小的细节相互协调作用而成的: -> “The details are not the details. They make the design.” ―Charles Eames -> “细节不只是细节,细节成就了设计。” +> “细节不只是细节,细节成就了设计。” —— Charles Eames -Thank you! 谢谢! -*Follow UX Planet:* [*Twitter*](https://twitter.com/101babich)[*Facebook*](https://www.facebook.com/uxplanet/) **关注 UX Planet:** [*Twitter*](https://twitter.com/101babich)[*Facebook*](https://www.facebook.com/uxplanet/) --- From 1004922aa7b7e87efced13f6b71a75d06f353a8b Mon Sep 17 00:00:00 2001 From: ivyxuan Date: Sun, 9 Apr 2017 21:14:26 +0800 Subject: [PATCH 095/638] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=B8=80=E7=82=B9?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO/the-details-that-matter.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/TODO/the-details-that-matter.md b/TODO/the-details-that-matter.md index fce6047f6df..b4ccfb5d9f8 100644 --- a/TODO/the-details-that-matter.md +++ b/TODO/the-details-that-matter.md @@ -62,7 +62,7 @@ > 在应用里写出好的文字信息,和让应用正常运行、用户界面易于使用一样重要。 -#### 让应用看起来像一个有血有肉的人 #### +#### 让应用看起来像是一个人 #### 有一个快速的方法能让你的 UI 变得温暖而不呆板,就是用人说话的口吻去描述内容。如果你的产品听起来好像是一个人,用户就会更加的信任你。 @@ -80,9 +80,7 @@ Airbnb 的提示听起来像人说的话而且语气还很随和。 -**像“出错啦”这种警告对所有的用户都会造成困扰,而且还会惹恼专家级用户。** - -但是,一个精心设计过的错误信息,会顿时化失望为欣喜。所以,把报错变得人性化、不用技术性的语言并且适合你的用户群体。 +**像“出错啦”这种警告对所有的用户都会造成困扰,而且还会惹恼专家级用户。**但是,一个精心设计过的错误信息,会顿时化失望为欣喜。所以,把报错变得人性化、不用技术性的语言并且适合你的用户群体。 @@ -144,13 +142,13 @@ Medium 是一个典型的例子,它善于使用留白来改善用户对内容 ### 总结 ### -**用心设计,应用界面上的每一个小的细节都值得细心揣摩,因为用户体验就是由这些小的细节相互协调作用而成的: +**用心设计**,应用界面上的每一个小的细节都值得细心揣摩,因为用户体验就是由这些小的细节相互协调作用而成的: > “细节不只是细节,细节成就了设计。” —— Charles Eames 谢谢! -**关注 UX Planet:** [*Twitter*](https://twitter.com/101babich)[*Facebook*](https://www.facebook.com/uxplanet/) +**关注 UX Planet:** [*Twitter*](https://twitter.com/101babich) | [*Facebook*](https://www.facebook.com/uxplanet/) --- > [掘金翻译计划](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)、[React](https://github.com/xitu/gold-miner#react)、[前端](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)。 From d8e709c9c21142931a6e1e91b87c1971f8d4ccaa Mon Sep 17 00:00:00 2001 From: Ruixi Date: Sun, 9 Apr 2017 22:47:28 +0800 Subject: [PATCH 096/638] =?UTF-8?q?=E7=BF=BB=E8=AF=91=E5=88=9D=E7=A8=BF040?= =?UTF-8?q?9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ion-our-thoughts-on-design-are-changing.md | 66 +++++++++---------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/TODO/from-form-to-function-our-thoughts-on-design-are-changing.md b/TODO/from-form-to-function-our-thoughts-on-design-are-changing.md index f2bce0a9d4d..3ed924e8fd9 100644 --- a/TODO/from-form-to-function-our-thoughts-on-design-are-changing.md +++ b/TODO/from-form-to-function-our-thoughts-on-design-are-changing.md @@ -1,96 +1,96 @@ > * 原文地址:[From Form to Function, Our Thoughts On Design Are Changing](https://medium.com/thinking-design/from-form-to-function-our-thoughts-on-design-are-changing-ed556d8f2b58) > * 原文作者:[Adobe Creative Cloud](https://medium.com/@creativecloud) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 译者: +> * 译者:[Ruixi](https://github.com/ruixi) > * 校对者: -# From Form to Function, Our Thoughts On Design Are Changing +# 从形式到功能,设计思维的改变 ![](https://cdn-images-1.medium.com/max/1600/1*bImmCpF6MPs9JslB21eAVQ.jpeg) -Design is more important to business than ever before, because it’s an essential driver of user engagement. Looking at the recent evolution of user interface design might give you a sense of how user expectations have been changing and what’s coming next. But before we dive into details we need to find an answer to one important question — *what is design?* +设计作为用户参与的基本驱动力,对商业而言比以往任何时候都更加重要。留意一下最近的用户界面设计趋势,这可能让你感受到用户的期待以往的变化,以及未来的趋势。但在我们深入讨论之前,有个问题需要回答—— **设计是什么?** -### What is Design? +### 设计是什么? -Most people (even some designers) perceive design as visual elements that are added to a product after it’s done; a process that comes at the end of a product’s development, and is treated like a decoration that the designers slapped onto the real work of the engineers. While design is visual aesthetics, it is also much more. As Steve Jobs once said, “Design is not just what it looks like and feels like. *Design is how it works.*” Design is both how product ***looks*** and how it ***works*** . +大多数人(甚至包括一些设计师)都将设计视作在产品完成之后所添加的视觉点缀,在产品开发结束之后的工艺流程,就像是设计师们强加在工程师的真实工作之上的。设计的确是视觉美学,但不仅如此。就像史蒂夫·乔布斯曾经说过的:“设计不只关乎视觉和感官。**设计关乎(产品)如何运作**。(Design is not just what it looks like and feels like. Design is how it works.)” 设计既包括产品的**视觉体验**,也包括产品的**运作原理**。 -### The Evolution of Graphical User Interface (GUI) Design +### 图形用户界面(GUI)设计的演化 -Computers and humans don’t speak the same language. To make interaction possible, designers rely on *graphical* user interfaces. The recent evolution of GUI makes it clear that *design trends are evolving for users.* In order to prove this point let’s examine the GUI changes in the last decade. +计算机和人类说着不同的语言。设计师们依靠**图形**用户界面使交互成为可能。最近的 GUI 设计演化清晰地表明:**设计趋势就是用户的不断变化。** 为了证明这一点,我们来看看最近十年的 GUI 设计变化。 -#### From Complexity to Simplicity +#### 从复杂到简单 -Towards the end of the 2000’s GUI design started to change significantly due to the rise in popularity of mobile devices. This huge shift in device preference led to designers having to rethink interfaces from scratch, which in turn led to global changes in GUI design. +由于移动设备的普及,2000 年底的 GUI 设计开始产生了明显的变化。设备配置的极大变化迫使设计师们不得不界面进行重新思考,而这又反过来引起了全球的 GUI 设计的变化。 -Looking at the history of the web, we can see that a decade ago websites were rudimentary in terms of design. But visual appearance wasn’t the only problem in this approach to design. Websites tried to provide *as many options as possible* : all the information a site contained seemed to be available; everything included on a site was ‘equally’ important. Designers thought that it would make websites more valuable to users. Unfortunately this often led to a cluttered interface. In the example below, you can see how distracting a cluttered interface can be from a usability standpoint. +看看 Web 的历史,我们就会发现十年前的网站设计是不成熟的。但视觉并不是这种设计方式唯一的问题所在。网站试图提供**尽可能多的选择**:一个站点所包含的所有信息似乎都是可用的,页面上的每个元素都“同等”重要。(那时的)设计师们觉得这样会让网站对用户来说更有价值。不幸的是,这往往导致页面的杂乱无章。在下方的案例中,你可以看出,从可用性的角度出发,一个乱七八糟的界面能凌乱到什么程度。 ![](https://cdn-images-1.medium.com/max/1600/1*MgAzj4RVV2zFTQKQCmfSRw.jpeg) -With the rise of mobile devices designers began to realize that user attention is a precious resource and should be treated accordingly. This lead to the *highly focused* and *very prioritized interfaces*. Such interfaces have just the right amount of information available at the exactly right time users need it. +随着移动设备的兴起,设计师们开始意识到,用户的注意力是一种需要被合理利用的珍贵资源。这就促使了**高度集中**和**按优先级处理界面**的出现。这样的界面能在用户最需要的时候提供最恰当数量的可用信息。 ![](https://cdn-images-1.medium.com/max/1600/1*IMwAqnMVH2peTtdNtwPPhQ.jpeg) -#### From Skeuomorphism to Flat Design +#### 从拟物化到扁平化 -Do you remember when all touch screen apps looked very physical? At one point in time, almost all apps used a skeuomorphic design style which was based on symbols borrowed from the real world. +你还记得触屏上的所有应用程序看起来都像真的一样的时候吗?那个时候,几乎所有应用程序都在使用拟物化设计风格,而这种风格又是对现实世界中符号的借用。 ![](https://cdn-images-1.medium.com/max/1600/1*Mrt--PeX7t5qPmnmwzDUAg.png) -Skeuomorphism wasn’t just a pure design trend, it played an important role in usability. When touch screen devices were fairly new to many users, designers had to make sure users would understand how apps worked. Skeuomorphic designs help users understand how to use a new interface works by making the design familiar. That’s why the iOS Newsstand app in the example above looks like a physical bookshelf. As users progressively became more familiar with touch screens, such design metaphors weren’t needed and this style went away. +拟物化并不是一种纯粹的设计趋势,它在可用性方面也扮演了重要的角色。当很多用户都对触屏设备知之甚少的时候,设计师们必须确保用户能够明白这些应用程序是做什么的。拟物化设计通过让设计更加贴近(现实生活)来帮助人们理解新页面的运作方式。这就是上方的 iOS 报刊亭应用看上去就像是一个真实的书架(的原因)。随着用户渐渐的熟悉了触屏,这种设计隐喻显得鸡肋,而这种设计风格也逐渐退出了历史舞台。 ![](https://cdn-images-1.medium.com/max/1600/1*TxE-vVFUv_61nYtdml78Kw.jpeg) -With the quick adoption of new technologies, there was a movement towards a pure digital look, called flat design. This new style relies mainly on flat textures and icons, typography, spacing, and color to bring order to the digital canvas. +随着新技术的使用,一种纯粹的数字化外观出现了:扁平化。这种新的设计风格主要依靠平面质感、图标、排版、间距和色彩来营造数字界面的秩序。 -#### From a Single Channel to Seamless Experience Across All Channels +#### 从单一终端到跨终端 -Ten years ago a major design challenge was to make sure designs worked in every browser. Today, the major design challenge is to *make sure your designworks on the devices your users use*. There’s no such thing as mobile user and desktop user any more. There are users who may want to use your product in the same way, regardless of the device. That’s why *continuity across multiple devices* — creating a seamless experience across mobile, desktop, tablet and wearables — is so important. The goal is to put users at the center of your multi-channel design, [offering an omnichannel approach](https://uxmag.com/articles/5-elements-of-omni-channel-user-experiences) that lets users efficiently use product regardless of the device. +十年前,一个主要的设计挑战就是保证设计能够在每一个浏览器下正常运行。今天,主要的设计挑战是**保证你的设计在用户所使用的设备上正常运行**。不再有移动用户和桌面端用户这种说法了。只有无论在什么设备上都可能想要照常使用你的产品的用户。这就是为什么**跨终端体验**(在移动端、桌面端、平板和可穿戴设备上的无缝体验)如此重要。目标就是将用户放在你的设计(包括多种终端)之中,[提供一个全方位的解决方案](https://uxmag.com/articles/5-elements-of-omni-channel-user-experiences) ,能够让用户无论在何种设备上都能够高效地使用产品。 ![](https://cdn-images-1.medium.com/max/1600/1*j5kqBjTpLFS5e1J3wkmvgw.jpeg) -### From Pixels to People +### 从像素到人 -Modern apps and sites are more than just the graphical representation of a solution — they are complex systems focused on solving user problems and generating valuable outcome. Despite all advantages, these systems have a serious natural barrier — graphical user interface. No matter how good a GUI is, people still have to learn to use it. In order to solve this problem modern UX goes beyond on-screen design and into a world of UI-less interactions. +现代应用程序和网站不只是解决方案的视觉表现,更是专注于解决用户问题并提供有价值结果的复杂系统。尽管这些系统有种种优点,但它还是有一个严重的天然障碍——图形用户界面。无论 GUI 如何优秀,人们都不得不去学习如何使用它。为了解决这个问题,现代 UX 走得更远——屏幕设计,以及融入没有用户界面的交互之中。 -#### Time-Saving Design +#### 节省时间的设计 -Today, users expect more user-devoted, frictionless experiences from their interactions with technology. They want to use products designed to save their time. Since time-saving design is all about respecting a user’s time, it’s clear why it’s on the rise. Modern apps strive to follow this trend by: +今天,用户期望从他们与科技的交互中的用户投入更多,以及经验的无需磨合。他们想要使用被设计来节省他们的时间的产品。节约时间的设计完全是对用户时间的慎重对待,这也清楚地解释了它们崛起的原因。当代应用程序设计力求追随如下趋势: -- **Anticipating user needs** Take Dark Sky weather app for instance. Some users might still prefer to open a weather app to check the forecast, but the most useful thing the weather app can do is to alert user about suddenly changed weather conditions (e.g. notify user that it’ll be snowing soon). +- **预见用户需求。** 这里用 Dark Sky weather 举个例子。一些用户可能仍然喜欢打开天气应用来查看天气预报,但天气应用最有用的举动是提醒用户突然变化的天气状况(比如通知用户很快就要下雪了)。 ![](https://cdn-images-1.medium.com/max/1600/1*79Wbi92BeDyVaDPEEQjMLQ.jpeg) -- **Interpreting user actions and goals.** When you open the Uber app for Apple Watch, it goes straight to a screen showing how long it’ll be until a car can come get you — no pulling out your phone to drop pins required. +- **理解用户行为和目标。** 当你打开 Apple Watch 的优步应用时,它会直接在屏幕上显示车辆能够到达的时间——不需要拿出手机选择定位。 ![](https://cdn-images-1.medium.com/max/1600/0*0ouzEuTHaORKAryP.jpg) -#### Self-learning systems (SLS) +#### 自学习系统(Self-learning systems ,SLS) -Self learning systems (SLS) powered software anticipates tasks that need to be done and simply auto-completes them for the user, or at least gets the user several steps closer to finishing the tasks. Software that functions more autonomously has a major benefit for the users — it requires *much less attention*. The basic building block of self-learning software is the ability for a system to learn based on experience, analyse incoming data, and take action in response to new events. The challenge with SLS is to design behaviours based on the fewest possible interactions, while focusing on people’s behavior. Why is it a challenge? Because you need to find a balance between saving your user’s time and providing just enough options so users feel that they have control over a system. +自学习系统控制软件预见一些简单的、需要被自动代替用户完成的任务,或者至少让用户更接近任务完成的步骤。功能更为自主的软件对用户来说有个很重大的益处——不需要**太多注意力**。 自学习软件的基本构件是基于经验的系统学习能力,分析传入数据,以及对新的事件作出反应。自学习系统面临的挑战是在控制交互行为数量的基础上设计行为,关注人的行为。为什么这是个挑战?因为你需要在节约用户时间和为用户提供足够多的选项(让用户感受到自己对系统的控制)找到一个平衡点 ![](https://cdn-images-1.medium.com/max/1600/1*_yiHy0NAU1xANCf6HYxMDA.jpeg) -[Nest](https://nest.com/) is great example of SLS. It’s a semi-intelligent thermostat that can program itself around user’s life. Each time a user changes the settings, Nest remembers temperature adjustments, and after a few days users will be adjusting Nest less, because it pulls all it has learned into a schedule for the home. Yes, Nest has a lot of downsides (the most critical one is that [the system often lives its own way](https://www.nngroup.com/articles/emotional-design-fail/)), but still it’s a great example of the next wave of product. Looking ahead, self-learning software will be the one thing that distinguishes legacy apps from modern ones. +[Nest](https://nest.com/) 就是个很好的自学习系统的例子。它是一个可以围绕用户的生活来制定计划的半智能恒温器。每当用户更改系统设置的时候,Nest 都会记得调整温度,并且在几天之后用户对 Nest 的调整会减少,因为它把它学到的一切都变成家庭的时间表。是的,Nest 有很多缺点(最严重的是[系统总是采用自己的方式](https://www.nngroup.com/articles/emotional-design-fail/)),但它依然是下一代产品的绝好示例。展望未来,自学习软件将会是区别传统应用和现代应用的一个重要因素。 -#### Conversational Interfaces +#### 会话界面 -With the advent of iPhone Messages, Slack or WhatsApp, the way we exchange information changed irreversibly. Text messages have become an extremely natural way of communicating. +随着 iPhone 消息, Slack 以及 WhatsApp 的出现,我们交换信息的方式被完全改变。 短信已经成为一种极其自然的交流方式。 ![](https://cdn-images-1.medium.com/max/1600/1*ZMmEYOSW_mZttqHXIEqDpw.png) -This trend led to the popularization of conversational interfaces. Essentially, a conversational interface is any user interface that mimics chatting with a real human. “Chatbot” is one of the hottest terms in our industry right now. More and more apps are leaving behind the GUI in favor of personal chat. Why? Because conversation feels natural for us and this property makes the use of chatbots much more intuitive than tapping on a bunch of buttons in traditional user interfaces. Another benefit of conversational interfaces is in detalization: GUI can only have a finite amount of options in practice, but speaking to a chatbot can in theory (if designed well) allow open ended discovery and interaction. +这一趋势导致了会话界面的普及。从本质上讲,会话界面就是是模仿真实人类聊天的用户界面。“聊天机器人(Chatbot)”是目前我们行业最热门的术语之一。越来越多的应用程序的 GUI 支持个人聊天。为什么?因为交谈对我们来说感觉很自然,这种特性使聊天机器人的使用比在传统用户界面中敲击一堆按钮更加直观。另一个会话界面的优势在于细节程度(这个词我不太确定——译者注):GUI 在实际中只有有限的选项,但和聊天机器人交谈在理论上(如果设计得好)允许开放式的发现和交互。 ![](https://cdn-images-1.medium.com/max/1600/1*odt0dGAYbEau2LXZo8EF3A.gif) -Last but not least, teens and millennials — who represent the bulk of tomorrow’s market — spend more time on messaging apps than on any other apps or sites, creating a huge opportunity for businesses who want to reach this audience. +最后但同样重要,青少年和千禧一代——未来市场的主要代表——在短信应用程序中花的时间比任何其他应用程序或网站都要多,为想要接触到这个群体的(人/企业)创造了一个巨大的商业机会。 -But chatbots aren’t a final step of conversational interface evolution. Voice interfaces will be a natural next step for chatbots. In the not too distant future, voice interaction will make up a large part of how we interact with the technology around us. The experience of using voice commands to control computers has already been transformed by a new generation of voice-interaction systems such as Apple’s Siri, Google Now and Amazon Echo. The latter doesn’t use a traditional GUI as a means of interaction. But the biggest challenge is to understand how people will interact with voice interfaces. This requires a better understanding of humans — not only the topics they are interested in talking about, but *how* they are going to talk about the topics. +但聊天机器人不是会话界面进化的最终形态。语音界面将会是聊天机器人的下一步。在不远的将来,语音交互将会在我们与技术的互动中占据很大部分。使用语音命令来控制计算机已经由新一代语音交互软件,比如 Siri,Google Now 和 Amazon Echo 实现了。它们不使用传统的 GUI 作为交互手段。但最大的挑战是了解人们将如何与语音界面进行交互。这需要更好地了解人类——不仅是他们感兴趣的话题,还包括他们将**如何**谈论这些话题。 ![](https://cdn-images-1.medium.com/max/1600/1*coZE_xgldZTEQyI7SCdkvg.jpeg) -### Conclusion +### 结语 -With technology continuously evolving, we are on a path that could make interaction with digital services more intuitive, more accessible, and more efficient. Next generation platforms continue to develop much more like human-to-human conversation. The interface of the future might not always be made of pixels. +随着技术的不断发展,我们正在使互动与数字服务更直观,更便捷,更高效的道路上不断前行。下一代平台继续发展得更接近人与人之间的对话。未来的界面不一定是由像素组成的。 --- From 0075d9a6d2d3a1f1cfad38b54f0f4f7a51015e3e Mon Sep 17 00:00:00 2001 From: ace Date: Mon, 10 Apr 2017 10:06:48 +0800 Subject: [PATCH 097/638] =?UTF-8?q?update:=20=E4=B8=89=E6=A0=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO/react-is-slow-react-is-fast.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TODO/react-is-slow-react-is-fast.md b/TODO/react-is-slow-react-is-fast.md index f2c44574c00..2188b60bd4d 100644 --- a/TODO/react-is-slow-react-is-fast.md +++ b/TODO/react-is-slow-react-is-fast.md @@ -171,7 +171,7 @@ class DatagridBody extends Component { export default DatagridBody; ``` -**小提示**:相比手工实现 `shouldComponentUpdate()` 方法,我可以继承 React 的 `PureComponent` 而不是 `Component`。这个组件会用严格对等(`===`)对比所有的 props,并且仅当 **任一** props 变更时重绘。但是我知道在例子的上下文中 `resource` 和 `children` 不不会变更,所以无需检查他们的对等性。 +**小提示**:相比手工实现 `shouldComponentUpdate()` 方法,我可以继承 React 的 `PureComponent` 而不是 `Component`。这个组件会用严格对等(`===`)对比所有的 props,并且仅当 **任一** props 变更时重绘。但是我知道在例子的上下文中 `resource` 和 `children` 不会变更,所以无需检查他们的对等性。 有了这一优化,点击表头后,`` 组件的重绘会跳过表体及其全部 231 个组件。这会将 500ms 的更新时间减少到 60ms。网络性能提高超过 400ms! @@ -488,7 +488,7 @@ export default onlyUpdateForKeys(['basePath', 'refresh'])(Toolbar); ## 结论 -还有许多可以使 React 应用更快的方法(使用 keys,懒加载重路由,`react-addons-perf` 包,使用 ServiceWorkers 缓存应用状态,使用同构等等),但正确实现 `shouldComponentUpdate` 是第一步 - 也是最有用的。 +还有许多可以使 React 应用更快的方法(使用 keys、懒加载重路由、`react-addons-perf` 包、使用 ServiceWorkers 缓存应用状态、使用同构等等),但正确实现 `shouldComponentUpdate` 是第一步 - 也是最有用的。 React 默认是不快的,但是无论是什么规模的应用,它都提供了许多工具来加速。这也许是违反直觉的,尤其自从许多框架提供了 React 的替代品,它们声称比 React 快 n 倍。但 React 把开发者的体验放在了性能之前。这也是为什么用 React 开发大型应用是个愉快的体验,没有惊吓,只有不变的实现速度。 From 41b103a78d20e01e81aa9013b816cab4180785c0 Mon Sep 17 00:00:00 2001 From: lsvih Date: Mon, 10 Apr 2017 13:44:43 +0800 Subject: [PATCH 098/638] =?UTF-8?q?=E4=B8=80=E6=A0=A1=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO/webpack-and-rollup-the-same-but-different.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/TODO/webpack-and-rollup-the-same-but-different.md b/TODO/webpack-and-rollup-the-same-but-different.md index 4e286f35689..b80a7886547 100644 --- a/TODO/webpack-and-rollup-the-same-but-different.md +++ b/TODO/webpack-and-rollup-the-same-but-different.md @@ -8,7 +8,7 @@ ![](https://cdn-images-1.medium.com/max/1000/1*rtjClMZ8sq3cLFT9Aq8Xyg.png) -本周,Facebook 将一个[非常大的 pull request](https://github.com/facebook/react/pull/9327) 合并到了 React 主分支。这个 PR 将 React 以前使用的构建工具替换成了 [Rollup](https://rollupjs.org/)。这让许多人感到不解,纷纷在推特上提问:“为什么你们选择 Rollup 而不选择 Webpack 呢?”[1](https://twitter.com/stanlemon/status/849366789825994752) [2](https://twitter.com/MrMohtas/status/849362334988595201) [3](https://twitter.com/kyleholzinger/status/849683292760797184) +本周,Facebook 将一个[非常大的 pull request](https://github.com/facebook/react/pull/9327) 合并到了 React 主分支。这个 PR 将 React 当前使用的构建工具替换成了 [Rollup](https://rollupjs.org/)。这让许多人感到不解,纷纷在推特上提问:“为什么你们选择 Rollup 而不选择 Webpack 呢?”[1](https://twitter.com/stanlemon/status/849366789825994752) [2](https://twitter.com/MrMohtas/status/849362334988595201) [3](https://twitter.com/kyleholzinger/status/849683292760797184) 有人问这个问题是很正常的。[Webpack](https://webpack.js.org/) 是现在 JavaScript 社区中最伟大的成功传奇之一,它有着数百万/月的下载量,驱动了成千上万的网站与应用。它有着巨大的生态系统、众多的贡献者,并且它与一般的社区开源项目不同——它有着[意义非凡的经济支持](https://opencollective.com/webpack)。 @@ -16,9 +16,9 @@ ### 这两个打包工具的优缺点 ### -Webpack 由 [Tobias Koppers](https://medium.com/@sokra) 在 2012 年创建,用于解决当时的工具不能处理的问题:构建复杂的单页应用(SPA)。它的两个特点改变了一切: +Webpack 由 [Tobias Koppers](https://medium.com/@sokra) 在 2012 年创建,用于解决当时的工具不能处理的问题:构建复杂的单页应用(SPA)。尤其是它的两个特点改变了一切: -1. **代码分割**可以将你的 app 分割成许多个容易管理的分块,这些分块能够在用户使用你的 app 时按需加载。这意味着你的网站可以比那些没有使用此技术的网站要快上很多。因为访问那些网站必须要等待整个应用都被下载并解析完成。当然,你**也可以**自己手动去进行代码分割,但是……总之,祝你好运。 +1. **代码分割**可以将你的 app 分割成许多个容易管理的分块,这些分块能够在用户使用你的 app 时按需加载。这意味着你的用户可以有更快的交互体验。因为访问那些网站必须要等待整个应用都被下载并解析完成。当然,你**也可以**自己手动去进行代码分割,但是……总之,祝你好运。 2. **静态资源**的导入:图片、CSS 等静态资源可以直接导入到你的 app 中,就和其它的模块、节点一样能够进行依赖管理。因此,我们再也不用小心翼翼地将各个静态文件放在特定的文件夹中,然后再去用脚本给文件 URL 加上哈希串了。Webpack 已经帮你完成了这一切。 而 Rollup 的开发理念则不同:它利用 ES2015 模块的巧妙设计,尽可能高效地构建精简且易分发的 JavaScript 库。而其它的模块打包器(包括 Webpack在内)都是通过将模块分别封装进函数中,然将这些函数通过能在浏览器中实现的 `require` 方法打包,最后依次处理这些函数。在你需要实现按需加载的时候,这种做法非常的方便,但是这样做引入了很多无关代码,比较浪费资源。当[你有很多模块要打包的时候,这种情况会变得更糟糕](https://nolanlawson.com/2016/08/15/the-cost-of-small-modules/)。 @@ -29,21 +29,21 @@ ES2015 模块则启用了一种不同的实现方法,Rollup 用的也就是这 ### 那么我到底应该选用哪一个呢? ### -回答这个问题之前,我们已经了解了这两个工具各自的优缺点,它们同时存在并相互支持的原因正是因为它们解决的问题不同。那么,现在这个问题的答案简单来说就是: +到目前为止,我们已经清晰地了解了这两个工具共存并且相互支撑的原因 — 它们应用于不同的场景。那么,现在这个问题的答案简单来说就是: > 在开发应用时使用 Webpack,开发库时使用 Rollup 当然这不是什么严格的规定——有很多的网站和 app 一样是使用 Rollup 构建的,同时也有很多的库使用 Webpack。不过,这是个很值得参考的经验之谈。 -如果你需要进行代码分割,或者你有很多的静态资源,再或者你做的东西深度依赖 CommonJS,毫无疑问 Webpack 是你的最佳选择。如果你的代码基于 ES2015 模块编写,并且你做的东西是准备给他人使用的,你应该更适合用 Rollup。 +如果你需要进行代码分割,或者你有很多的静态资源,再或者你做的东西深度依赖 CommonJS,毫无疑问 Webpack 是你的最佳选择。如果你的代码基于 ES2015 模块编写,并且你做的东西是准备给他人使用的,你或许可以考虑使用 Rollup。 ### 对于包作者的建议:请使用 `pkg.module`! ### -在很长一段时间里,使用 JavaScript 库是一件相当有风险的事,因为这意味着你必须和库的作者在模块系统上的意见保持一致。如果你使用 Browserify 而他更喜欢 AMD,你就不得不在 build 之前先强行将两者粘起来。[通用模块定义(UMD)](https://github.com/umdjs/umd)格式对这个问题进行了 *部分* 的修复,但是它没有强制要求所有的人都用它,最后反而会让你更加不知所措。 +在很长一段时间里,使用 JavaScript 库是一件有点风险的事,因为这意味着你必须和库的作者在模块系统上的意见保持一致。如果你使用 Browserify 而他更喜欢 AMD,你就不得不在 build 之前先强行将两者粘起来。[通用模块定义(UMD)](https://github.com/umdjs/umd)格式对这个问题进行了 **部分** 的修复,但是它没有强制要求在任何场景下都使用它,因此你无法预料你将会遇到什么坑。 ES2015 改变了这一切,因为 `import` 与 `export` 就是语言规范本身的一部分。在未来,不再会有现在这种模棱两可的情况,所有东西都将更加无缝地配合工作。不幸的是,由于大多数浏览器和 Node 还不支持 `import` 和 `export`,我们仍然需要依靠 UMD 规范(如果你只写 Node 的话也可以用 CommonJS)。 -现在给你的库的 package.json 文件增加一个 `"module": "dist/my-library.es.js"` 入口,可以让你的库同时支持 UMD 与 ES2015。**这很重要,因为 Webpack 和 Rollup 都使用了 ** `pkg.module` ** 来尽可能的生成效率更高的代码**——在一些情况下,它们都能使用 [tree-shake](https://webpack.js.org/guides/tree-shaking/) 来精简掉你的库中未使用的部分。 +现在给你的库的 package.json 文件增加一个 `"module": "dist/my-library.es.js"` 入口,可以让你的库同时支持 UMD 与 ES2015。**这很重要,因为 Webpack 和 Rollup 都使用了 `pkg.module` 来尽可能的生成效率更高的代码**——在一些情况下,它们都能使用 [tree-shake](https://webpack.js.org/guides/tree-shaking/) 来精简掉你的库中未使用的部分。 *了解更多有关 `pkg.module` 的内容请访问 [Rollup wiki](https://github.com/rollup/rollup/wiki/pkg.module) 。* From b4d215686cd9c950fd952a64b27468bc6a75d4b8 Mon Sep 17 00:00:00 2001 From: zhouzihanntu Date: Mon, 10 Apr 2017 15:09:31 +0800 Subject: [PATCH 099/638] =?UTF-8?q?=E6=A0=B9=E6=8D=AE=E7=AC=AC=E4=BA=8C?= =?UTF-8?q?=E4=BD=8D=E6=A0=A1=E5=AF=B9=E8=80=85=E6=84=8F=E8=A7=81=E4=BF=AE?= =?UTF-8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...modules-in-node-js-everything-you-need-to-know.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/TODO/requiring-modules-in-node-js-everything-you-need-to-know.md b/TODO/requiring-modules-in-node-js-everything-you-need-to-know.md index d48fe1071ac..afdf951dfad 100644 --- a/TODO/requiring-modules-in-node-js-everything-you-need-to-know.md +++ b/TODO/requiring-modules-in-node-js-everything-you-need-to-know.md @@ -15,9 +15,9 @@ Node 提供了两个核心模块来管理模块依赖: - `require` 模块在全局范围内可用,不需要写 `require('require')`. - `module` 模块同样在全局范围内可用,不需要写 `require('module')`. -你可以将 `require` 模块理解为命令,将 `module` 模块理解为所有必要模块的组织者。 +你可以将 `require` 模块理解为命令,将 `module` 模块理解为所有引入模块的组织者。 -在 Node 中引入一个模块其实并没有想象中的那么复杂。 +在 Node 中引入一个模块其实并不是个多么复杂的概念。 ``` const config = require('/path/to/file'); @@ -29,7 +29,7 @@ const config = require('/path/to/file'); - **加载**:确定文件内容的类型。 - **打包**:为文件划分私有作用域,这样 `require` 和 `module` 两个对象对于我们要引入的每个模块来说就都是本地的。 - **评估**:最后由虚拟机对加载得到的代码做评估。 -- **缓存**:当再次引用该文件时,无需再重复以上所有步骤。 +- **缓存**:当再次引用该文件时,无需再重复以上步骤。 在本文中,我将尝试举例说明这些不同阶段的工作原理,以及它们是如何影响我们在 Node 中编写模块的方式的。 @@ -62,7 +62,7 @@ Module { Node 模块与文件系统中的文件有着一对一的关系。我们通过加载模块对应的文件内容到内存中来实现模块引用。 -然而,由于 Node 允许使用许多方式引入文件(例如,使用相对路径或预先配置的路径),我们需要在将文件的内容加载到内存前找到该文件的绝对位置。 +然而,由于 Node 允许使用多种方式引入文件(例如,使用相对路径或预先配置的路径),我们需要在将文件的内容加载到内存前找到该文件的绝对位置。 例如,我们不声明路径,直接引入一个 `'find-me'` 模块时: @@ -389,7 +389,7 @@ fs.readFile('/etc/passwd', (err, data) => { 我们现在来回答关于 Node 中循环依赖的重要问题:当我们在模块1中引用模块2,在模块2中引用模块1时会发生什么? -为了找到答案,我们在 `lib/` 下创建 `module1.js` 和 `module.js` 两个文件并让它们互相引用: +为了找到答案,我们在 `lib/` 下创建 `module1.js` 和 `module2.js` 两个文件并让它们互相引用: ``` // lib/module1.js @@ -643,7 +643,7 @@ if (require.main === module) { } ``` -所以我们可以使用这个条件来满足以上的使用需求,通过不同的方式调用 printInFrame 函数。 +所以我们可以使用该条件判断来满足上述使用需求,通过不同的方式调用 printInFrame 函数。 ``` // 在 print-in-frame.js 中 From 63a5a48054b1f5fcaf5cce5c283d554209bd2b19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=B9=E5=8F=B7=E4=B8=89?= Date: Mon, 10 Apr 2017 15:15:42 +0800 Subject: [PATCH 100/638] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=8E=88=E6=9D=83?= =?UTF-8?q?=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...bpack-bits-getting-the-most-out-of-the-commonschunkplugin.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO/webpack-bits-getting-the-most-out-of-the-commonschunkplugin.md b/TODO/webpack-bits-getting-the-most-out-of-the-commonschunkplugin.md index cd003ab1e9f..21e2cff04aa 100644 --- a/TODO/webpack-bits-getting-the-most-out-of-the-commonschunkplugin.md +++ b/TODO/webpack-bits-getting-the-most-out-of-the-commonschunkplugin.md @@ -1,5 +1,5 @@ > * 原文地址:[webpack bits: Getting the most out of the CommonsChunkPlugin()](https://medium.com/webpack/webpack-bits-getting-the-most-out-of-the-commonschunkplugin-ab389e5f318#.hn8v7ul1f) -> * 原文作者:[Sean T. Larkin](https://medium.com/@TheLarkInn?source=post_header_lockup) +> * 原文作者:本文已获原作者 [Sean T. Larkin](https://medium.com/@TheLarkInn) 授权 > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 译者:[reid3290](https://github.com/reid3290) > * 校对者: From 2421f9334d1b28b4b0232bca842b3a35542d943b Mon Sep 17 00:00:00 2001 From: zhouzihanntu Date: Mon, 10 Apr 2017 15:37:42 +0800 Subject: [PATCH 101/638] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=A0=A1=E5=AF=B9?= =?UTF-8?q?=E8=80=85=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../requiring-modules-in-node-js-everything-you-need-to-know.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO/requiring-modules-in-node-js-everything-you-need-to-know.md b/TODO/requiring-modules-in-node-js-everything-you-need-to-know.md index afdf951dfad..d160796cd96 100644 --- a/TODO/requiring-modules-in-node-js-everything-you-need-to-know.md +++ b/TODO/requiring-modules-in-node-js-everything-you-need-to-know.md @@ -2,7 +2,7 @@ > * 原文作者:[Samer Buna](https://medium.freecodecamp.com/@samerbuna?source=post_header_lockup) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 译者:[zhouzihanntu](https://github.com/zhouzihanntu) -> * 校对者:[lsvih](https://github.com/lsvih) +> * 校对者:[lsvih](https://github.com/lsvih), [reid3290](https://github.com/reid3290) # 关于在 Node.js 中引用模块,知道这些就够了 # From 9fb579dbd77e063247690279625b8b49a0b3367a Mon Sep 17 00:00:00 2001 From: DeepMissea <398752853@qq.com> Date: Mon, 10 Apr 2017 15:53:10 +0700 Subject: [PATCH 102/638] =?UTF-8?q?=E6=A0=B9=E6=8D=AE=E4=B8=A4=E4=BD=8D?= =?UTF-8?q?=E6=A0=A1=E5=AF=B9=E8=80=85=E7=9A=84=E6=84=8F=E8=A7=81=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO/mvvmc-with-swift.md | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/TODO/mvvmc-with-swift.md b/TODO/mvvmc-with-swift.md index 0e68011078c..01134ab233a 100644 --- a/TODO/mvvmc-with-swift.md +++ b/TODO/mvvmc-with-swift.md @@ -12,18 +12,18 @@ # 简介 -现今,iOS 开发者面临的最大挑战是构建一个健壮应用程序,它必须易于维护、测试和扩展。 +现今,iOS 开发者面临的最大挑战是构建一个健壮的应用程序,它必须易于维护、测试和扩展。 在这篇文章里,你会学到一种可靠的方法来达到目的。 -首先,需要简介一下你即将学习的内容: +首先,简要介绍下你即将学习的内容: **架构模式**. # 架构模式 ## 它是什么 -> 架构模式是给定上下文中软件体系结构中常见的,可重用的解决方案。架构与软件设计模式相似,但作用范围更广。架构解决了软件工程中的各种问题,如计算机硬件性能限制,高可用性和最小化业务风险。一些架构模式已经在软件框架内实现。 +> 架构模式是给定上下文中软件体系结构中常见的,可重用的解决方案。架构与软件设计模式相似,但涉及的范围更广。架构解决了软件工程中的各种问题,如计算机硬件性能限制,高可用性和最小化业务风险。一些架构模式已经在软件框架内实现。 摘自 [Wikipedia](https://en.wikipedia.org/wiki/Architectural_pattern)。 @@ -31,31 +31,31 @@ ## 主要的模式 -在项目中,有几种可用的架构模式,并且你可以在项目中使用多个,因为每个模式都能高好的适应特定的场景。 +在项目中,有几种可用的架构模式,并且你可以在项目中使用多个,因为每个模式都能更好地适应特定的场景。 -当你阅读关于这几种模式时,主要会遇到这种: +当你阅读这几种模式时,主要会遇到: ### [Model-View-Controller](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) ![](https://marcosantadev.com/wp-content/uploads/mvc_2.jpg) -这是最常见的,也许在你的第一个 iOS 应用中已经使用过。不幸地是,这也是最糟糕的模式,因为 `Controller` 不得不管理每一个依赖(API、数据库等等),包括你应用的业务逻辑,而且与 `UIKit` 的耦合度很高,这意味着测试异常的艰难。 +这是最常见的,也许在你的第一个 iOS 应用中已经使用过。不幸地是,这也是最糟糕的模式,因为 `Controller` 不得不管理每一个依赖(API、数据库等等),包括你应用的业务逻辑,而且与 `UIKit` 的耦合度很高,这意味着很难去测试。 -你通常会避免这种模式,用下面的某种来代替它。 +你应该避免这种模式,用下面的某种来代替它。 ### [Model-View-Presenter](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93presenter) ![](https://marcosantadev.com/wp-content/uploads/mvp.jpg) -这是第一个 MVC 的替代方案之一,一次对 `Controller` 和 `View` 之间解耦的很好的尝试。 +这是第一个 MVC 模式的备选方案之一,一次对 `Controller` 和 `View` 之间解耦的很好的尝试。 -在 MVP 中,你有一层叫做 `Presenter` 的新结构来处理业务逻辑。而 `View` —— 你的 `UIViewController` 以及任何 `UIKit` 组件,都是一个笨的对象,他们只通过 `Presenter` 更新,并在触发 UI 事件的时候,负责通知 `Presenter`。由于 `Presenter` 没有任何 `UIKit` 的引用,所以非常容易测试。 +在 MVP 中,你有一层叫做 `Presenter` 的新结构来处理业务逻辑。而 `View` —— 你的 `UIViewController` 以及任何 `UIKit` 组件,都是一个笨的对象,他们只通过 `Presenter` 更新,并在 UI 事件被触发的时候,负责通知 `Presenter`。由于 `Presenter` 没有任何 `UIKit` 的引用,所以非常容易测试。 ### [Viper](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93presenter) [![](https://www.objc.io/images/issue-13/2014-06-07-viper-intro-0a53d9f8.jpg)](https://www.objc.io/issues/13-architecture/viper/) -这是 [Bob 叔叔清晰架构](https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html)的代表。 +这是 [Bob 叔叔的清晰架构](https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html)的代表。 这种模式的强大之处在于,它合理分配了不同层次之间的职责。通过这种方式,你的每个层次做的的事变得很少,易于测试,并且具备单一职责。这种模式的问题是,在大多数场合里,它过于复杂。你需要管理很多层,这会让你感到混乱,难于管理。 @@ -65,7 +65,7 @@ ![](https://marcosantadev.com/wp-content/uploads/mvvm.jpg) -最后但也是最重要的,MVVM 是一个类似于 MVP 的框架,因为层次看几乎相同。你可以认为 MVVM 是 MVP 版本的一个进化,而这得益于 UI 绑定。 +最后但也是最重要的,MVVM 是一个类似于 MVP 的框架,因为层级结构几乎相同。你可以认为 MVVM 是 MVP 版本的一个进化,而这得益于 UI 绑定。 UI 绑定是在 `View` 和 `ViewModel` 之间建立一座单向或双向的桥梁,并且两者之间以一种非常透明地方式进行沟通。 @@ -73,7 +73,7 @@ UI 绑定是在 `View` 和 `ViewModel` 之间建立一座单向或双向的桥 在 Swift 里有多种方式实现 UI 绑定: -#### RxSwift (or ReactiveCocoa) +#### RxSwift (或 ReactiveCocoa) [RxSwift](https://github.com/ReactiveX/RxSwift) 是 [ReactiveX](http://reactivex.io/) 家族的一个 Swift 版本的实现。一旦你掌握了它,你就能很轻松地切换到 RxJava、RxJavascript 等等。 @@ -97,7 +97,7 @@ class ViewController: UIViewController { 我不会解释如何彻底地使用 RxSwift,因为这超出本文的目标,它自己会有文章来解释。 -FRP 让你学习到了一种新的方式来开发,你可能对它或爱或恨。如果你没用过 FRP 开发,那你不得不花费几小时的时间来使用,并理解如何正确的使用,因为它是一个完全不同的编程概念。 +FRP 让你学习到了一种新的方式来开发,你可能对它或爱或恨。如果你没用过 FRP 开发,那你需要花费几个小时来熟悉和理解如何正确地使用它,因为它是一个完全不同的编程概念。 另一个类似于 RxSwift 的框架是 [ReactiveCocoa](https://github.com/ReactiveCocoa/ReactiveCocoa),如果你想了解他们之间主要的区别的话,你可以看看[这篇文章](https://www.raywenderlich.com/126522/reactivecocoa-vs-rxswift)。 @@ -211,7 +211,7 @@ class ViewModel { ## 抉择: MVVM-C -在你不得不选择一个架构模式时,你需要理解哪一种更适合你的需求。在这些模式里,MVVM 是最好的选择,因为它强大的同时,也易于使用。 +在你不得不选择一个架构模式时,你需要理解哪一种更适合你的需求。在这些模式里,MVVM 是最好的选择之一,因为它强大的同时,也易于使用。 不幸地是这种模式并不完美,主要的缺陷是 MVVM 没有路由管理。 @@ -225,7 +225,7 @@ class ViewModel { 你可以在[这里](https://github.com/MarcoSantarossa/MVVM-C_with_Swift)下载项目源码。 -这些类被简化了,以便于你可以专注于 MVVM-C 是如何工作的,因此 GitHub 上的类可能会有轻微出入。 +这个例子被简化了,以便于你可以专注于 MVVM-C 是如何工作的,因此 GitHub 上的类可能会有轻微出入。 示例应用是一个普通的仪表盘应用,它从公共 API 获取数据,一旦数据准备就绪,用户就可以通过 ID 查找实体,如下面的截图: @@ -294,7 +294,7 @@ final class DashboardContainerCoordinator: Coordinator { } ``` -你一定能注意到在 `Coordinator` 里,一个父类 `UIViewController` 对象或者子类对象,类似于 `UINavigationController`,被注入到构造器之中。因为 `Coordinator` 有责任添加 `View` 到视图层级之中,它必须知道那个父类添加了 `View`。 +你一定能注意到在 `Coordinator` 里,一个父类 `UIViewController` 对象或者子类对象,比如 `UINavigationController`,被注入到构造器之中。因为 `Coordinator` 有责任添加 `View` 到视图层级之中,它必须知道那个父类添加了 `View`。 在上面的例子里,`DashboardContainerCoordinator` 实现了协议 `Coordinator`: @@ -337,7 +337,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { 在 `AppDelegate` 里,我们实例化一个新的 `DashboardContainerCoordinator`,通过 `start` 方法,我们把新的视图推入 `navigationController` 里。 -**你可以看到 GitHub 项目如何注入一个 `UINavigationController` 类型的对象,并去除 `UIKit` 和 `Coordinator` 之间的耦合。** +**你可以看到在 GitHub 上的项目是如何注入一个 `UINavigationController` 类型的对象,并去除 `UIKit` 和 `Coordinator` 之间的耦合。** ### Model @@ -450,7 +450,7 @@ final class UsersViewModel { MVVM-C 有很多优点,可以提高应用程序的质量。你应该注意使用哪种方式来进行 UI 绑定,因为 RxSwift 不容易掌握,而且如果你不明白你做的是什么,调试和测试有时可能会有点棘手。 -我的建议是一点点地开始使用这种架构模式,这样你可以对不同的层次得到使用,并且能保证层次之间的良好的分离,易于测试。 +我的建议是一点点地开始使用这种架构模式,这样你可以学习不同层次的使用,并且能保证层次之间的良好的分离,易于测试。 # FAQ @@ -466,7 +466,7 @@ MVVM-C 有很多优点,可以提高应用程序的质量。你应该注意使 这取决于你要开新项目,还是要维护旧代码。在有遗留代码的项目中,你可能无法使用 RxSwift,因为你需要重构很多的类。如果你有时间和资源来做,我建议你新开一项目一点一点的做,否则还是尝试其他的方法来解决 UI 绑定的问题。 -需要考虑的一个重要事情是,RxSwift 会一直是你项目中的另一个依赖,你可能会因为 RxSwift 的突破性改动而导致浪费时间的风险,或者缺少要在边缘案例中实现功能的文档。 +需要考虑的一个重要事情是,RxSwift 最终会成为你项目中的另一个依赖,你可能会因为 RxSwift 的破坏性改动而导致浪费时间的风险,或者缺少要在边缘案例中实现功能的文档。 --- From 575abaa19e6fc5bf58452a64699a00e0d62bf6fa Mon Sep 17 00:00:00 2001 From: DeepMissea <398752853@qq.com> Date: Mon, 10 Apr 2017 15:55:20 +0700 Subject: [PATCH 103/638] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=A0=A1=E5=AF=B9?= =?UTF-8?q?=E8=80=85=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO/mvvmc-with-swift.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO/mvvmc-with-swift.md b/TODO/mvvmc-with-swift.md index 01134ab233a..eb77db7fd87 100644 --- a/TODO/mvvmc-with-swift.md +++ b/TODO/mvvmc-with-swift.md @@ -2,7 +2,7 @@ > * 原文作者:[Marco Santarossa](https://marcosantadev.com/) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 译者:[Deepmissea](http://deepmissea.blue) -> * 校对者: +> * 校对者:[atuooo](http://atuo.xyz),[1992chenlu](https://github.com/1992chenlu) --- From 59b306ee920a7a4ffa66bb66396b88012751398f Mon Sep 17 00:00:00 2001 From: reid Date: Mon, 10 Apr 2017 17:45:38 +0800 Subject: [PATCH 104/638] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=A0=A1=E5=AF=B9?= =?UTF-8?q?=E8=80=85=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...bpack-bits-getting-the-most-out-of-the-commonschunkplugin.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO/webpack-bits-getting-the-most-out-of-the-commonschunkplugin.md b/TODO/webpack-bits-getting-the-most-out-of-the-commonschunkplugin.md index cd003ab1e9f..e9b092a47c2 100644 --- a/TODO/webpack-bits-getting-the-most-out-of-the-commonschunkplugin.md +++ b/TODO/webpack-bits-getting-the-most-out-of-the-commonschunkplugin.md @@ -2,7 +2,7 @@ > * 原文作者:[Sean T. Larkin](https://medium.com/@TheLarkInn?source=post_header_lockup) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 译者:[reid3290](https://github.com/reid3290) -> * 校对者: +> * 校对者:[avocadowang](https://github.com/avocadowang),[Aladdin-ADD](https://github.com/Aladdin-ADD) # webpack 拾翠:充分利用 CommonsChunkPlugin() # From 961859c7975344537dd677404afe6b0a67fb74b1 Mon Sep 17 00:00:00 2001 From: tanglie Date: Mon, 10 Apr 2017 22:07:05 +0800 Subject: [PATCH 105/638] Update Dependency-Injection-with-Dagger-2.md --- TODO/Dependency-Injection-with-Dagger-2.md | 26 +++++++++++----------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/TODO/Dependency-Injection-with-Dagger-2.md b/TODO/Dependency-Injection-with-Dagger-2.md index e36788e6667..304ecaf4d00 100644 --- a/TODO/Dependency-Injection-with-Dagger-2.md +++ b/TODO/Dependency-Injection-with-Dagger-2.md @@ -2,17 +2,17 @@ > * 原文作者:[CodePath](https://github.com/codepath) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 译者: [tanglie1993](https://github.com/tanglie1993) -> * 校对者: +> * 校对者:[mnikn](https://github.com/mnikn), [Zhiw](https://github.com/Zhiw) # 用 Dagger 2 实现依赖注入 ## 概要 -很多 Android 应用依赖于一些含有其它依赖的对象。例如,一个 Twitter API 客户端可能需要通过 [Retrofit](https://github.com/codepath/android_guides/wiki/Consuming-APIs-with-Retrofit) 之类的网络库被构建。要使用这个库,你可能还需要添加 [Gson](https://github.com/codepath/android_guides/wiki/Leveraging-the-Gson-Library) 这样的解析库。另外,实现认证或缓存的库可能需要使用 [shared preferences](https://github.com/codepath/android_guides/wiki/Storing-and-Accessing-SharedPreferences) 或其它通用存储方式。这就需要先把它们实例化,并创建一个隐含的依赖链。 +很多 Android 应用依赖于一些含有其它依赖的对象。例如,一个 Twitter API 客户端可能需要通过 [Retrofit](https://github.com/codepath/android_guides/wiki/Consuming-APIs-with-Retrofit) 之类的网络库来构建。要使用这个库,你可能还需要添加 [Gson](https://github.com/codepath/android_guides/wiki/Leveraging-the-Gson-Library) 这样的解析库。另外,实现认证或缓存的库可能需要使用 [shared preferences](https://github.com/codepath/android_guides/wiki/Storing-and-Accessing-SharedPreferences) 或其它通用存储方式。这就需要先把它们实例化,并创建一个隐含的依赖链。 如果你不熟悉依赖注入,看看[这个](https://www.youtube.com/watch?v=IKD2-MAkXyQ)短视频。 -Dagger 2 为你解析这些依赖,并生成把它们绑定在一起的代码。也有很多其它的 Java 依赖注入框架,但它们中很多个是有缺陷的,比如依赖 XML,需要在运行时验证依赖,或者在起始时造成性能负担。 [Dagger 2](http://google.github.io/dagger/) 纯粹依赖于 Java [注解解析器](https://www.youtube.com/watch?v=dOcs-NKK-RA)以及编译时检查来分析并验证依赖。它被认为是目前最高效的依赖注入框架之一。 +Dagger 2 为你解析这些依赖,并生成把它们绑定在一起的代码。也有很多其它的 Java 依赖注入框架,但它们中大多数是有缺陷的,比如依赖 XML,需要在运行时验证依赖,或者在起始时造成性能负担。 [Dagger 2](http://google.github.io/dagger/) 纯粹依赖于 Java [注解解析器](https://www.youtube.com/watch?v=dOcs-NKK-RA)以及编译时检查来分析并验证依赖。它被认为是目前最高效的依赖注入框架之一。 ### 优点 @@ -31,7 +31,7 @@ public class MainActivity extends Activity { } ``` - * **容易配置复杂的依赖**。 对象创建是有隐含顺序的。Dagger 2 浏览依赖图,并且[生成易于理解和追踪的代码](https://github.com/codepath/android_guides/wiki/Dependency-Injection-with-Dagger-2#code-generation)。而且,它可以节约大量的样板代码,使你不再需要手写,手动获取引用并把它们传递给其他对象作为依赖。它也简化了重构,因为你可以聚焦于构建模块本身,而不是它们被创建的顺序。 + * **容易配置复杂的依赖关系**。 对象创建是有隐含顺序的。Dagger 2 遍历依赖关系图,并且[生成易于理解和追踪的代码](https://github.com/codepath/android_guides/wiki/Dependency-Injection-with-Dagger-2#code-generation)。而且,它可以节约大量的样板代码,使你不再需要手写,手动获取引用并把它们传递给其他对象作为依赖。它也简化了重构,因为你可以聚焦于构建模块本身,而不是它们被创建的顺序。 * **更简单的单元和集成测试** 因为依赖图是为我们创建的,我们可以轻易换出用于创建网络响应的模块,并模拟这种行为。 @@ -41,7 +41,7 @@ public class MainActivity extends Activity { 默认的 Android Studio 不把生成的 Dagger 2 代码视作合法的类,因为它们通常并不被加入 source 路径。但引入 `android-apt` 插件后,它会把这些文件加入 IDE classpath,从而提供更好的可见性。 -确保[升级](https://github.com/codepath/android_guides/wiki/Getting-Started-with-Gradle#upgrading-gradle) 到最迟的 Gradle 版本以使用最新的 `annotationProcessor` 语法: +确保[升级](https://github.com/codepath/android_guides/wiki/Getting-Started-with-Gradle#upgrading-gradle) 到最新的 Gradle 版本以使用最新的 `annotationProcessor` 语法: ```gradle dependencies { @@ -202,7 +202,7 @@ public interface NetComponent { #### 生成代码 -Dagger 2 的一个重要特点是它会为标注 `@Component` 的接口生成类的代码。你可以使用带有 `Dagger` (比如 `DaggerTwitterApiComponent.java`) 前缀的类来为依赖图提供实例,并用它来完成用 `@Inject` 注解的域的注入。 参见[[setup guide|Dependency-Injection-with-Dagger-2#setup]]。 +Dagger 2 的一个重要特点是它会为标注 `@Component` 的接口生成类的代码。你可以使用带有 `Dagger` (比如 `DaggerTwitterApiComponent.java`) 前缀的类来为依赖图提供实例,并用它来完成用 `@Inject` 注解的域的注入。 参见[设置](https://github.com/xitu/gold-miner/pull/1484#%E8%AE%BE%E7%BD%AE)。 ### 实例化组件 @@ -382,7 +382,7 @@ public interface GitHubComponent { } ``` -假定 Github 模块只是把 API 接口返回给 github API: +假定 Github 模块只是把 API 接口返回给 Github API: ```java @@ -417,7 +417,7 @@ public interface NetComponent { } ``` -最终的步骤是用 `GitHubComponent` 进行实例化。这一次,我们需要首先实现 `NetComponent` 并把它传递给 `DaggerGitHubComponent` 建造者的构造方法: +最终的步骤是用 `GitHubComponent` 进行实例化。这一次,我们需要首先实现 `NetComponent` 并把它传递给 `DaggerGitHubComponent` builder 的构造方法: ```java NetComponent mNetComponent = DaggerNetComponent.builder() @@ -498,12 +498,12 @@ public class MyActivity extends Activity { } ``` -#### 子组件建造者 +#### 子组件 builder *从 v2.7 版本起可用* -![Dagger 子组件建造者](https://raw.githubusercontent.com/codepath/android_guides/master/images/subcomponent_builders.png) +![Dagger 子组件 builder](https://raw.githubusercontent.com/codepath/android_guides/master/images/subcomponent_builders.png) -子组件建造者使创建子组件的类和子组件的父类解耦。这是通过移除父组件中的子组件工厂方法实现的。 +子组件 builder 使创建子组件的类和子组件的父类解耦。这是通过移除父组件中的子组件工厂方法实现的。 ```java @MyActivityScope @@ -521,7 +521,7 @@ public interface SubcomponentBuilder { } ``` -子组件是在子组件接口内部的接口中声明的。它必须含有一个  `build()` 方法,其返回值和子组件相匹配。用这个方法声明一个基接口是很方便的,就像上面的`SubcomponentBuilder` 一样。这个新的**建造者必须被加入父组件的图中**,而这是用一个 "binder" 模块和一个 "subcomponents" 参数实现的: +子组件是在子组件接口内部的接口中声明的。它必须含有一个  `build()` 方法,其返回值和子组件相匹配。用这个方法声明一个基接口是很方便的,就像上面的`SubcomponentBuilder` 一样。这个新的 **builder 必须被加入父组件的图中**,而这是用一个 "binder" 模块和一个 "subcomponents" 参数实现的: ```java @Module(subcomponents={ MyActivitySubComponent.class }) @@ -544,7 +544,7 @@ public @interface SubcomponentKey { } ``` -一旦建造者在出现在组件图中,activity 就可以用它来创建子组件: +一旦 builder 在出现在组件图中,activity 就可以用它来创建子组件: ```java public class MyActivity extends Activity { From b5bc496940ed9f61b97ec13d0c21fd9844045b84 Mon Sep 17 00:00:00 2001 From: Tuccuay Date: Mon, 10 Apr 2017 22:31:50 +0800 Subject: [PATCH 106/638] quote --- TODO/mastering-swift-essential-details-about-strings.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/TODO/mastering-swift-essential-details-about-strings.md b/TODO/mastering-swift-essential-details-about-strings.md index 325223b5681..1abd697d093 100644 --- a/TODO/mastering-swift-essential-details-about-strings.md +++ b/TODO/mastering-swift-essential-details-about-strings.md @@ -541,4 +541,6 @@ print(numberOfStars) // => 2 **对于字符操作你有没有找到更舒适的方法?写下评论我们一起来讨论一些吧!** -**P.S.** 不知道你有没有兴趣阅读我的另一篇文章:[detailed overview of array and dictionary literals in Swift](https://rainsoft.io/concise-initialization-of-collections-in-swift/) \ No newline at end of file +**P.S.** 不知道你有没有兴趣阅读我的另一篇文章:[detailed overview of array and dictionary literals in Swift](https://rainsoft.io/concise-initialization-of-collections-in-swift/) + +>[掘金翻译计划](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)、[React](https://github.com/xitu/gold-miner#react)、[前端](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)。 \ No newline at end of file From 6b3ffdc1a0481ea0e9aa9cde2298129ac52041cf Mon Sep 17 00:00:00 2001 From: zhuzi Date: Mon, 10 Apr 2017 22:50:34 +0800 Subject: [PATCH 107/638] update according to proof-reading --- TODO/beyond-browser-web-desktop-apps.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/TODO/beyond-browser-web-desktop-apps.md b/TODO/beyond-browser-web-desktop-apps.md index 9803dd3b2c6..1899ee8f1d9 100644 --- a/TODO/beyond-browser-web-desktop-apps.md +++ b/TODO/beyond-browser-web-desktop-apps.md @@ -2,7 +2,7 @@ > * 原文作者:本文已获原作者 [Adam Lynch](https://www.smashingmagazine.com/author/adamlynch/) 授权 > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 译者: [bambooom](https://github.com/bambooom)/[imink](https://github.com/imink) -> * 校对者:[bambooom](https://github.com/bambooom)/[sunui](https://github.com/sunui) +> * 校对者:[bambooom](https://github.com/bambooom)/[imink](https://github.com/imink)/[sunui](https://github.com/sunui) ## 超越浏览器:从 web 应用到桌面应用 @@ -12,7 +12,7 @@ 别慌,深呼吸,现实情况是,作为 web 开发者,你已经拥有开发现代桌面应用所需的一切技能,得益于新的强大的 API,你甚至可以在桌面应用中发挥你最大的潜能。 -本文将会介绍使用 [NW.js](http://nwjs.io/) 和 [Electron](https://electron.atom.io/) 开发桌面应用,它们的优劣,使用同一套代码库给桌面、web,甚至更多。 +本文将会介绍使用 [NW.js](http://nwjs.io/) 和 [Electron](https://electron.atom.io/) 开发桌面应用,包括它们的优劣,以及如何使用同一套代码库来开发桌面、web 应用,甚至更多。 ### 为什么? @@ -22,7 +22,7 @@ 很难总结为什么你应该考虑开发桌面应用,因为真的有很多类型的应用你可以创建。这非常取决于你想要达到什么目的,API 是否足够有利于开发,离线使用将多大程度上增强用户体验。在我的团队,这些都是毋庸置疑的,因为我们在开发一个[聊天应用程序](https://teamwork.com/chat)。另一方面来说,一个依赖于网络而没有任何与系统集成的桌面应用应该做成一个 web 应用,并且只做 web 应用。当用户并不能从桌面应用中获得比在浏览器中访问一个网址更多的价值的时候,期待用户下载你的应用(其中自带浏览器以及 Node.js)是不公平的。 -比起描述你个人应该建造的桌面应用及其原因,我更希望的是激发一个想法,或者只是激发你对这篇文章的兴趣。继续往下读来看看用 web 技术构造一个强大的桌面应用是多么简单,以及创建完成(或者在这过程中)一个 web 应用你需要承受什么。。 +比起描述你个人应该建造的桌面应用及其原因,我更希望的是激发一个想法,或者只是激发你对这篇文章的兴趣。继续往下读来看看用 web 技术构造一个强大的桌面应用是多么简单,以及在创建过程中你应该付出什么。 ### NW.js @@ -81,7 +81,7 @@ Chromium 有一个主要的后台进程,每个标签页也会有自己的进 就是这么简单。NW.js 初始化了第一个窗口,加载了你的 HTML 文件,虽然这看起来并没有什么,但接下来就是你来添加标签及样式了,就和在 web 应用中一样。 -你可以凭自己喜好去掉窗口栏,构建自己的框架模板。你可以有半透明或全透明的窗口,可以有隐藏窗口或者更多。我最近试了一下用 NW.js 复活了[回形针](http://engineroom.teamwork.com/resurrecting-clippy/) Office 助手(一般昵称 Clippy)。能看到它同时在 macOS 或者 Windows 10 中复活有种奇妙的满足感。 +你可以凭自己喜好去掉窗口栏,构建自己的框架模板。你可以有半透明或全透明的窗口,可以有隐藏窗口或者更多。我最近尝试使用 NW.js 做了[Clippy](http://engineroom.teamwork.com/resurrecting-clippy/)(Office 助手)。能在 macOS 和 Windows 10 上看到它有种奇妙的满足感。 ![Screenshot of clippy.desktop on macOS](https://www.smashingmagazine.com/wp-content/uploads/2017/01/clippy-preview-opt.png) @@ -217,11 +217,11 @@ NW.js 和 Electron 都支持很多平台,包括 Windows,Mac 和 Linux。Elec Electron 甚至支持 ARM 版本,所以你的 app 可以在 Chromebook 或者树莓派上运行,最终,Google 可能会[逐步淘汰 Chrome 封装应用 (Packaged App)](https://blog.chromium.org/2016/08/from-chrome-apps-to-web.html),但是 NW.js 仍然支持将应用程序移植到 NW.js 应用,并且仍然可以访问相同的 Chromium API。 -虽然 32 位和 64 位的版本都支持,你也可以使用 64 位的 Mac 和 Windows 应用。但是,为了兼容,32 位和 64 位 Linux 应用程序是都需要的。 +虽然 32 位和 64 位的版本都支持,所以你完全可以使用 64 位的 Mac 和 Windows 应用。但是,为了兼容,32 位和 64 位 Linux 应用程序是都需要的。 假如 Electron 胜出,你想发行一个 Electron 应用。有一个很不错的 Node.js 包叫 [electron-packager](https://github.com/electron-userland/electron-packager) 可以帮你将 app 打包成一个 `.app` 或者 `.exe` 文件。也有其他几个类似的项目,包括交互式的一步一步告诉你该怎么做。不过,你应该用 [electron-builder](https://github.com/electron-userland/electron-builder),它以 electron-packager 为基础,添加了其他几个相关的模块,生成的是 `.dmg` 文件和 Windows 安装包,并且为你处理好了代码签名的问题。这很重要,如果没有这一步,你的应用将会被操作系统认为是不可信的,你的应用程序可能会触发防毒软件的运行,Microsoft SmartScreen 可能会尝试阻止用户启动你的应用。 -关于代码签名的令人讨厌的事情是,你必须在 Mac 上为 Mac 和 Windows 上为 Windows 签署你的应用程序。因此,如果是认真要发行桌面应用的话,就需要为每个发行版本给多种机器构建。 +关于代码签名的令人讨厌的事情是,你必须单独为某个平台签名你的应用程序,比如在 Mac 上签名 Mac 应用,在 Windows 签名 Windows 应用。因此,如果你很在乎发行桌面应用的话,就必须为每个发行版本分别构建适用于不同平台的应用(以及分别签名)。 这可能会感到不够自动化很繁琐,特别是如果你习惯于在 web 上创建。幸运的是,electron-builder 被创造出来完成这些自动化工作。我说的是持续集成工具例如 [Jenkins](https://jenkins.io/),[CodeShip](http://codeship.com/),[Travis-CI](https://travis-ci.org/),[AppVeyor](https://www.appveyor.com/)(Windows 集成)等。这些工具可以让你按一个按钮或者每次更新代码到 GitHub 时重新构建你的桌面应用。 From 0e05d37cf28db7bc0282e66922aa56d1588ce5c7 Mon Sep 17 00:00:00 2001 From: sqrthree Date: Mon, 10 Apr 2017 23:04:02 +0800 Subject: [PATCH 108/638] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20Preload=EF=BC=8CPr?= =?UTF-8?q?efetch=20=E5=92=8C=E5=AE=83=E4=BB=AC=E5=9C=A8=20Chrome=20?= =?UTF-8?q?=E4=B9=8B=E4=B8=AD=E7=9A=84=E4=BC=98=E5=85=88=E7=BA=A7=20?= =?UTF-8?q?=E7=9A=84=E7=A7=AF=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 ++- front-end.md | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 066b76a852c..7e89841009a 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,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) 上的英文分享文章。内容覆盖 [Android](#android)、[iOS](#ios)、[React](#react)、[前端](#前端)、[后端](#后端)、[产品](#产品)、[设计](#设计) 等领域,读者为热爱新技术的新锐开发者。 -掘金翻译计划目前翻译完成 [448](#近期文章列表) 篇文章,共有 [270](https://github.com/xitu/gold-miner/wiki/%E8%AF%91%E8%80%85%E7%A7%AF%E5%88%86%E8%A1%A8) 余名译者贡献翻译。 +掘金翻译计划目前翻译完成 [449](#近期文章列表) 篇文章,共有 [270](https://github.com/xitu/gold-miner/wiki/%E8%AF%91%E8%80%85%E7%A7%AF%E5%88%86%E8%A1%A8) 余名译者贡献翻译。 # 官方指南: @@ -37,6 +37,7 @@ ## 前端 +* [Preload,Prefetch 和它们在 Chrome 之中的优先级](https://juejin.im/post/58e8acf10ce46300585a7a42?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([gy134340](http://gy134340.com/) 翻译) * [setState() 门事件](https://juejin.im/post/58e5aeccb123db15eb80ecb0?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([reid3290](https://github.com/reid3290) 翻译) * [如何使用 JavaScript 构建响应式引擎 —— Part 2:计算属性和依赖追踪](https://juejin.im/post/58ddeb1a570c3500579016ef?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([IridescentMia](https://github.com/IridescentMia) 翻译) * [如何使用 JavaScript 构建响应式引擎 —— Part 1:可观察的对象](https://juejin.im/post/58dc9da661ff4b0061547ca0?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([IridescentMia](https://github.com/IridescentMia) 翻译) diff --git a/front-end.md b/front-end.md index e38e54951be..3a2df9cf268 100644 --- a/front-end.md +++ b/front-end.md @@ -1,3 +1,4 @@ +* [Preload,Prefetch 和它们在 Chrome 之中的优先级](https://juejin.im/post/58e8acf10ce46300585a7a42?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([gy134340](http://gy134340.com/) 翻译) * [setState() 门事件](https://juejin.im/post/58e5aeccb123db15eb80ecb0?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([reid3290](https://github.com/reid3290) 翻译) * [如何使用 JavaScript 构建响应式引擎 —— Part 2:计算属性和依赖追踪](https://juejin.im/post/58ddeb1a570c3500579016ef?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([IridescentMia](https://github.com/IridescentMia) 翻译) * [如何使用 JavaScript 构建响应式引擎 —— Part 1:可观察的对象](https://juejin.im/post/58dc9da661ff4b0061547ca0?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([IridescentMia](https://github.com/IridescentMia) 翻译) From 41cb0f72b78d66bde60349afd8257b160f40b9c6 Mon Sep 17 00:00:00 2001 From: sqrthree Date: Mon, 10 Apr 2017 23:57:52 +0800 Subject: [PATCH 109/638] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20CSS=E5=BE=88?= =?UTF-8?q?=E6=A3=92=EF=BC=8C=E5=8F=AA=E6=98=AF=E7=9C=9F=E7=9A=84=E5=A4=AA?= =?UTF-8?q?=E9=9A=BE=E4=BA=86=20=E7=9A=84=E7=A7=AF=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 ++- front-end.md | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7e89841009a..c7c8629cecf 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,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) 上的英文分享文章。内容覆盖 [Android](#android)、[iOS](#ios)、[React](#react)、[前端](#前端)、[后端](#后端)、[产品](#产品)、[设计](#设计) 等领域,读者为热爱新技术的新锐开发者。 -掘金翻译计划目前翻译完成 [449](#近期文章列表) 篇文章,共有 [270](https://github.com/xitu/gold-miner/wiki/%E8%AF%91%E8%80%85%E7%A7%AF%E5%88%86%E8%A1%A8) 余名译者贡献翻译。 +掘金翻译计划目前翻译完成 [450](#近期文章列表) 篇文章,共有 [270](https://github.com/xitu/gold-miner/wiki/%E8%AF%91%E8%80%85%E7%A7%AF%E5%88%86%E8%A1%A8) 余名译者贡献翻译。 # 官方指南: @@ -37,6 +37,7 @@ ## 前端 +* [CSS很棒,只是真的太难了](https://juejin.im/entry/58eae24a61ff4b0061a6a102?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([ZhangFe](https://github.com/ZhangFe) 翻译) * [Preload,Prefetch 和它们在 Chrome 之中的优先级](https://juejin.im/post/58e8acf10ce46300585a7a42?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([gy134340](http://gy134340.com/) 翻译) * [setState() 门事件](https://juejin.im/post/58e5aeccb123db15eb80ecb0?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([reid3290](https://github.com/reid3290) 翻译) * [如何使用 JavaScript 构建响应式引擎 —— Part 2:计算属性和依赖追踪](https://juejin.im/post/58ddeb1a570c3500579016ef?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([IridescentMia](https://github.com/IridescentMia) 翻译) diff --git a/front-end.md b/front-end.md index 3a2df9cf268..52e31749ac5 100644 --- a/front-end.md +++ b/front-end.md @@ -1,3 +1,4 @@ +* [CSS很棒,只是真的太难了](https://juejin.im/entry/58eae24a61ff4b0061a6a102?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([ZhangFe](https://github.com/ZhangFe) 翻译) * [Preload,Prefetch 和它们在 Chrome 之中的优先级](https://juejin.im/post/58e8acf10ce46300585a7a42?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([gy134340](http://gy134340.com/) 翻译) * [setState() 门事件](https://juejin.im/post/58e5aeccb123db15eb80ecb0?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([reid3290](https://github.com/reid3290) 翻译) * [如何使用 JavaScript 构建响应式引擎 —— Part 2:计算属性和依赖追踪](https://juejin.im/post/58ddeb1a570c3500579016ef?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([IridescentMia](https://github.com/IridescentMia) 翻译) From 9b8d36b6d710e3bc3411c12a2f9601c984112811 Mon Sep 17 00:00:00 2001 From: sqrthree Date: Tue, 11 Apr 2017 00:16:25 +0800 Subject: [PATCH 110/638] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20Android=20?= =?UTF-8?q?=E5=A6=82=E4=BD=95=E5=AE=9E=E7=8E=B0=E6=B0=94=E6=B3=A1=E9=80=89?= =?UTF-8?q?=E6=8B=A9=E5=8A=A8=E7=94=BB=20=E7=9A=84=E7=A7=AF=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 7 +++---- android.md | 1 + 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c7c8629cecf..dae41fd13fa 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,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) 上的英文分享文章。内容覆盖 [Android](#android)、[iOS](#ios)、[React](#react)、[前端](#前端)、[后端](#后端)、[产品](#产品)、[设计](#设计) 等领域,读者为热爱新技术的新锐开发者。 -掘金翻译计划目前翻译完成 [450](#近期文章列表) 篇文章,共有 [270](https://github.com/xitu/gold-miner/wiki/%E8%AF%91%E8%80%85%E7%A7%AF%E5%88%86%E8%A1%A8) 余名译者贡献翻译。 +掘金翻译计划目前翻译完成 [451](#近期文章列表) 篇文章,共有 [270](https://github.com/xitu/gold-miner/wiki/%E8%AF%91%E8%80%85%E7%A7%AF%E5%88%86%E8%A1%A8) 余名译者贡献翻译。 # 官方指南: @@ -20,10 +20,10 @@ ## Android +* [Android 如何实现气泡选择动画](https://juejin.im/post/58e5ec838d6d8100616d82e2/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([skyar2009](https://github.com/skyar2009) 翻译) * [一个人的 Android 开发](https://juejin.im/entry/58dca515b123db00603887fd/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([BoilerYao](https://github.com/BoilerYao) 翻译) * [我是如何做到在 5 分钟之内将应用大小减少 60% 的](https://juejin.im/post/58d9b6a1a22b9d0064719f9e/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([jifaxu](https://github.com/jifaxu) 翻译) * [拉模式和推模式,命令式和响应式 – 响应式编程 [Android RxJava2](这到底是什么):第二部分](https://juejin.im/entry/58d78547a22b9d006465ca57/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([XHShirley](https://github.com/XHShirley) 翻译) -* [离线支持:不再『稍后重试』](https://juejin.im/post/58d491a8128fe1006cb6e750/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([skyar2009](https://github.com/skyar2009) 翻译) * [所有 Android 译文>>](https://github.com/xitu/gold-miner/blob/master/android.md) @@ -41,8 +41,7 @@ * [Preload,Prefetch 和它们在 Chrome 之中的优先级](https://juejin.im/post/58e8acf10ce46300585a7a42?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([gy134340](http://gy134340.com/) 翻译) * [setState() 门事件](https://juejin.im/post/58e5aeccb123db15eb80ecb0?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([reid3290](https://github.com/reid3290) 翻译) * [如何使用 JavaScript 构建响应式引擎 —— Part 2:计算属性和依赖追踪](https://juejin.im/post/58ddeb1a570c3500579016ef?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([IridescentMia](https://github.com/IridescentMia) 翻译) -* [如何使用 JavaScript 构建响应式引擎 —— Part 1:可观察的对象](https://juejin.im/post/58dc9da661ff4b0061547ca0?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([IridescentMia](https://github.com/IridescentMia) 翻译) -* [生活在 JavaScript 之中:学习第二门语言的好处](https://juejin.im/post/58d908deac502e0058db544b/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([gy134340](http://gy134340.com/) 翻译) +* [如何使用 JavaScript 构建响应式引擎 —— Part 1:可观察的对象](https://juejin.im/post/58dc9da661ff4b0061547ca0?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) * [所有前端译文>>](https://github.com/xitu/gold-miner/blob/master/front-end.md) diff --git a/android.md b/android.md index 2c1b495bf9d..8b02a079727 100644 --- a/android.md +++ b/android.md @@ -1,3 +1,4 @@ +* [Android 如何实现气泡选择动画](https://juejin.im/post/58e5ec838d6d8100616d82e2/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([skyar2009](https://github.com/skyar2009) 翻译) * [一个人的 Android 开发](https://juejin.im/entry/58dca515b123db00603887fd/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([BoilerYao](https://github.com/BoilerYao) 翻译) * [我是如何做到在 5 分钟之内将应用大小减少 60% 的](https://juejin.im/post/58d9b6a1a22b9d0064719f9e/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([jifaxu](https://github.com/jifaxu) 翻译) * [拉模式和推模式,命令式和响应式 – 响应式编程 [Android RxJava2](这到底是什么):第二部分](https://juejin.im/entry/58d78547a22b9d006465ca57/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([XHShirley](https://github.com/XHShirley) 翻译) From 771a38fa640cf24e5ad6d75ee86e182e6476175b Mon Sep 17 00:00:00 2001 From: sqrthree Date: Tue, 11 Apr 2017 00:46:50 +0800 Subject: [PATCH 111/638] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20=E5=9C=A8=20Apache?= =?UTF-8?q?=20=E5=92=8C=20Nginx=20=E6=97=A5=E5=BF=97=E9=87=8C=E6=A3=80?= =?UTF-8?q?=E6=B5=8B=E7=88=AC=E8=99=AB=E6=9C=BA=E5=99=A8=E4=BA=BA=20?= =?UTF-8?q?=E7=9A=84=E7=A7=AF=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- backend.md | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index dae41fd13fa..62a56a84c1f 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,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) 上的英文分享文章。内容覆盖 [Android](#android)、[iOS](#ios)、[React](#react)、[前端](#前端)、[后端](#后端)、[产品](#产品)、[设计](#设计) 等领域,读者为热爱新技术的新锐开发者。 -掘金翻译计划目前翻译完成 [451](#近期文章列表) 篇文章,共有 [270](https://github.com/xitu/gold-miner/wiki/%E8%AF%91%E8%80%85%E7%A7%AF%E5%88%86%E8%A1%A8) 余名译者贡献翻译。 +掘金翻译计划目前翻译完成 [452](#近期文章列表) 篇文章,共有 [270](https://github.com/xitu/gold-miner/wiki/%E8%AF%91%E8%80%85%E7%A7%AF%E5%88%86%E8%A1%A8) 余名译者贡献翻译。 # 官方指南: @@ -56,10 +56,10 @@ ## 后端 +* [在 Apache 和 Nginx 日志里检测爬虫机器人](https://juejin.im/post/58ea5758ac502e4957c78808/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([luoyaqifei](https://github.com/luoyaqifei) 翻译) * [如何在 ChromeOS 下用 Go 搭建 Web 服务](https://juejin.im/post/58d9e1711b69e6006bc38b1a/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([xiaoyusilen](https://github.com/xiaoyusilen) 翻译) * [在你沉迷于包的海洋之前,还是了解一下运行时 Node.js 的本身](https://juejin.im/post/58cf4a3144d90400690b7be7/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([fghpdf](https://github.com/fghpdf) 翻译) * [Pull request review 的十大错误](https://juejin.im/post/58ce3b3e61ff4b006c988f63/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([luoyaqifei](https://github.com/luoyaqifei) 翻译) -* [震惊!RxJava 5 个不为人知的小秘密](https://juejin.im/post/58cb833b8ac247218c2632e5/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([skyar2009](https://github.com/skyar2009) 翻译) * [所有后端译文>>](https://github.com/xitu/gold-miner/blob/master/backend.md) ## 教程 diff --git a/backend.md b/backend.md index 2e7926a7cba..8b96be0ba3f 100644 --- a/backend.md +++ b/backend.md @@ -1,3 +1,4 @@ +* [在 Apache 和 Nginx 日志里检测爬虫机器人](https://juejin.im/post/58ea5758ac502e4957c78808/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([luoyaqifei](https://github.com/luoyaqifei) 翻译) * [如何在 ChromeOS 下用 Go 搭建 Web 服务](https://juejin.im/post/58d9e1711b69e6006bc38b1a/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([xiaoyusilen](https://github.com/xiaoyusilen) 翻译) * [在你沉迷于包的海洋之前,还是了解一下运行时 Node.js 的本身](https://juejin.im/post/58cf4a3144d90400690b7be7/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([fghpdf](https://github.com/fghpdf) 翻译) * [Pull request review 的十大错误](https://juejin.im/post/58ce3b3e61ff4b006c988f63/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([luoyaqifei](https://github.com/luoyaqifei) 翻译) From 6ea9fa02939c766668d981cb3682c6915e650786 Mon Sep 17 00:00:00 2001 From: sqrthree Date: Tue, 11 Apr 2017 01:00:14 +0800 Subject: [PATCH 112/638] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E5=8C=96=20vs.=20=E5=BE=AE=E6=9C=8D=E5=8A=A1=20=E7=9A=84?= =?UTF-8?q?=E7=A7=AF=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- backend.md | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 62a56a84c1f..2df4c508b86 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,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) 上的英文分享文章。内容覆盖 [Android](#android)、[iOS](#ios)、[React](#react)、[前端](#前端)、[后端](#后端)、[产品](#产品)、[设计](#设计) 等领域,读者为热爱新技术的新锐开发者。 -掘金翻译计划目前翻译完成 [452](#近期文章列表) 篇文章,共有 [270](https://github.com/xitu/gold-miner/wiki/%E8%AF%91%E8%80%85%E7%A7%AF%E5%88%86%E8%A1%A8) 余名译者贡献翻译。 +掘金翻译计划目前翻译完成 [453](#近期文章列表) 篇文章,共有 [270](https://github.com/xitu/gold-miner/wiki/%E8%AF%91%E8%80%85%E7%A7%AF%E5%88%86%E8%A1%A8) 余名译者贡献翻译。 # 官方指南: @@ -56,10 +56,10 @@ ## 后端 +* [模块化 vs. 微服务](https://juejin.im/post/58eb2627da2f60005f0b2d60/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([lsvih](https://github.com/lsvih) 翻译) * [在 Apache 和 Nginx 日志里检测爬虫机器人](https://juejin.im/post/58ea5758ac502e4957c78808/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([luoyaqifei](https://github.com/luoyaqifei) 翻译) * [如何在 ChromeOS 下用 Go 搭建 Web 服务](https://juejin.im/post/58d9e1711b69e6006bc38b1a/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([xiaoyusilen](https://github.com/xiaoyusilen) 翻译) * [在你沉迷于包的海洋之前,还是了解一下运行时 Node.js 的本身](https://juejin.im/post/58cf4a3144d90400690b7be7/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([fghpdf](https://github.com/fghpdf) 翻译) -* [Pull request review 的十大错误](https://juejin.im/post/58ce3b3e61ff4b006c988f63/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([luoyaqifei](https://github.com/luoyaqifei) 翻译) * [所有后端译文>>](https://github.com/xitu/gold-miner/blob/master/backend.md) ## 教程 diff --git a/backend.md b/backend.md index 8b96be0ba3f..909e6435309 100644 --- a/backend.md +++ b/backend.md @@ -1,3 +1,4 @@ +* [模块化 vs. 微服务](https://juejin.im/post/58eb2627da2f60005f0b2d60/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([lsvih](https://github.com/lsvih) 翻译) * [在 Apache 和 Nginx 日志里检测爬虫机器人](https://juejin.im/post/58ea5758ac502e4957c78808/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([luoyaqifei](https://github.com/luoyaqifei) 翻译) * [如何在 ChromeOS 下用 Go 搭建 Web 服务](https://juejin.im/post/58d9e1711b69e6006bc38b1a/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([xiaoyusilen](https://github.com/xiaoyusilen) 翻译) * [在你沉迷于包的海洋之前,还是了解一下运行时 Node.js 的本身](https://juejin.im/post/58cf4a3144d90400690b7be7/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([fghpdf](https://github.com/fghpdf) 翻译) From 8a4cb26b6b86066a9584e81025c2fe30f17553ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=B9=E5=8F=B7=E4=B8=89?= Date: Tue, 11 Apr 2017 10:12:45 +0800 Subject: [PATCH 113/638] Fix typos. --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 2df4c508b86..d83a7c5e1b9 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,6 @@ * [Preload,Prefetch 和它们在 Chrome 之中的优先级](https://juejin.im/post/58e8acf10ce46300585a7a42?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([gy134340](http://gy134340.com/) 翻译) * [setState() 门事件](https://juejin.im/post/58e5aeccb123db15eb80ecb0?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([reid3290](https://github.com/reid3290) 翻译) * [如何使用 JavaScript 构建响应式引擎 —— Part 2:计算属性和依赖追踪](https://juejin.im/post/58ddeb1a570c3500579016ef?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([IridescentMia](https://github.com/IridescentMia) 翻译) -* [如何使用 JavaScript 构建响应式引擎 —— Part 1:可观察的对象](https://juejin.im/post/58dc9da661ff4b0061547ca0?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) * [所有前端译文>>](https://github.com/xitu/gold-miner/blob/master/front-end.md) From d57c25645060c19dc1e98f953c2ed33b8656db70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=B9=E5=8F=B7=E4=B8=89?= Date: Tue, 11 Apr 2017 10:13:31 +0800 Subject: [PATCH 114/638] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E8=AF=91=E8=80=85?= =?UTF-8?q?=E4=BA=BA=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d83a7c5e1b9..445c5394d6d 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,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) 上的英文分享文章。内容覆盖 [Android](#android)、[iOS](#ios)、[React](#react)、[前端](#前端)、[后端](#后端)、[产品](#产品)、[设计](#设计) 等领域,读者为热爱新技术的新锐开发者。 -掘金翻译计划目前翻译完成 [453](#近期文章列表) 篇文章,共有 [270](https://github.com/xitu/gold-miner/wiki/%E8%AF%91%E8%80%85%E7%A7%AF%E5%88%86%E8%A1%A8) 余名译者贡献翻译。 +掘金翻译计划目前翻译完成 [453](#近期文章列表) 篇文章,共有 [300](https://github.com/xitu/gold-miner/wiki/%E8%AF%91%E8%80%85%E7%A7%AF%E5%88%86%E8%A1%A8) 余名译者贡献翻译。 # 官方指南: From f995c21c858bb1e6a988995537f3583cf6db71ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=B9=E5=8F=B7=E4=B8=89?= Date: Tue, 11 Apr 2017 10:46:48 +0800 Subject: [PATCH 115/638] Update preload-prefetch-and-priorities-in-chrome.md --- TODO/preload-prefetch-and-priorities-in-chrome.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO/preload-prefetch-and-priorities-in-chrome.md b/TODO/preload-prefetch-and-priorities-in-chrome.md index 5f13ea73d2b..6fc8912e434 100644 --- a/TODO/preload-prefetch-and-priorities-in-chrome.md +++ b/TODO/preload-prefetch-and-priorities-in-chrome.md @@ -1,5 +1,5 @@ > * 原文地址:[Preload, Prefetch And Priorities in Chrome](https://medium.com/reloading/preload-prefetch-and-priorities-in-chrome-776165961bbf) -> * 原文作者:[Addy Osmani](https://medium.com/@addyosmani?source=post_header_lockup) +> * 原文作者:本文已获原作者 [Addy Osmani](https://medium.com/@addyosmani) 授权 > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 译者:[gy134340](https://github.com/gy134340) > * 校对者:[IridescentMia](https://github.com/IridescentMia),[vuuihc](https://github.com/vuuihc) From 48e36d994a282a1d23321bd0a5564386a6871889 Mon Sep 17 00:00:00 2001 From: sqrthree Date: Tue, 11 Apr 2017 22:55:28 +0800 Subject: [PATCH 116/638] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BD=9C=E8=80=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2df4c508b86..b00acba8e90 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ * [Preload,Prefetch 和它们在 Chrome 之中的优先级](https://juejin.im/post/58e8acf10ce46300585a7a42?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([gy134340](http://gy134340.com/) 翻译) * [setState() 门事件](https://juejin.im/post/58e5aeccb123db15eb80ecb0?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([reid3290](https://github.com/reid3290) 翻译) * [如何使用 JavaScript 构建响应式引擎 —— Part 2:计算属性和依赖追踪](https://juejin.im/post/58ddeb1a570c3500579016ef?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([IridescentMia](https://github.com/IridescentMia) 翻译) -* [如何使用 JavaScript 构建响应式引擎 —— Part 1:可观察的对象](https://juejin.im/post/58dc9da661ff4b0061547ca0?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) +* [如何使用 JavaScript 构建响应式引擎 —— Part 1:可观察的对象](https://juejin.im/post/58dc9da661ff4b0061547ca0?utm_source=gold-miner&utm_medium=readme&utm_campaign=github)([IridescentMia](https://github.com/IridescentMia) 翻译) * [所有前端译文>>](https://github.com/xitu/gold-miner/blob/master/front-end.md) From 422f548bef95305ec1f87d3f48958113f675ff5e Mon Sep 17 00:00:00 2001 From: sqrthree Date: Tue, 11 Apr 2017 23:04:46 +0800 Subject: [PATCH 117/638] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index b494634b4e5..54648b75c5d 100644 --- a/README.md +++ b/README.md @@ -41,10 +41,7 @@ * [Preload,Prefetch 和它们在 Chrome 之中的优先级](https://juejin.im/post/58e8acf10ce46300585a7a42?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([gy134340](http://gy134340.com/) 翻译) * [setState() 门事件](https://juejin.im/post/58e5aeccb123db15eb80ecb0?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([reid3290](https://github.com/reid3290) 翻译) * [如何使用 JavaScript 构建响应式引擎 —— Part 2:计算属性和依赖追踪](https://juejin.im/post/58ddeb1a570c3500579016ef?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([IridescentMia](https://github.com/IridescentMia) 翻译) -<<<<<<< HEAD * [如何使用 JavaScript 构建响应式引擎 —— Part 1:可观察的对象](https://juejin.im/post/58dc9da661ff4b0061547ca0?utm_source=gold-miner&utm_medium=readme&utm_campaign=github)([IridescentMia](https://github.com/IridescentMia) 翻译) -======= ->>>>>>> 2ffa1a092c355fc606b7cfc27f14af24d385d679 * [所有前端译文>>](https://github.com/xitu/gold-miner/blob/master/front-end.md) From 7d63b83df1a22c2082cab44926079a3f08063d2a Mon Sep 17 00:00:00 2001 From: Tuccuay Date: Tue, 11 Apr 2017 23:47:00 +0800 Subject: [PATCH 118/638] revision for @atuooo --- ...g-swift-essential-details-about-strings.md | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/TODO/mastering-swift-essential-details-about-strings.md b/TODO/mastering-swift-essential-details-about-strings.md index 1abd697d093..14cd30aa1a8 100644 --- a/TODO/mastering-swift-essential-details-about-strings.md +++ b/TODO/mastering-swift-essential-details-about-strings.md @@ -2,7 +2,7 @@ * 原文作者:[Dmitri Pavlutin](https://rainsoft.io/author/dmitri-pavlutin/) * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) * 译者:[Tuccuay](https://www.tuccuay.com) -* 校对者: +* 校对者:[oOatuo](https://github.com/atuooo) # 掌握 Swift 的字符串细节 @@ -11,7 +11,7 @@ String 类型在任何编程语言中都是一个重要的组成部分。而用 为了触及更多的用户,iOS 应用必须国际化以支持大量现代语言。Unicode 标准解决了这个问题,不过这也给我们使用 string 类型带来了额外的挑战性。 -从一方面来说,编程语言应该在平衡处理字符串时应该在 Unicode 复杂性和性能之间取得平衡。而另一方面,它需要为开发者提供一个舒适的结构来处理字符串。 +从一方面来说,编程语言在处理字符串时应该在 Unicode 复杂性和性能之间取得平衡。而另一方面,它需要为开发者提供一个舒适的结构来处理字符串。 而在我看来,Swift 在这两方面都做的不错。 @@ -23,16 +23,16 @@ Swift 对此有着更好的实现方式。字符串本身不再是集合,而 对于 `let myStr = "Hello, world"` 来说,你可以访问到下面这些 view: -- `myStr.characters` 即 `String.CharacterView`。可以获取字形的值,视觉上呈现为单一的符号。是最常用的试图 +- `myStr.characters` 即 `String.CharacterView`。可以获取字形的值,视觉上呈现为单一的符号,是最常用的视图。 - `myStr.unicodeScalars` 即 `String.UnicodeScalarView`。可以获取 21 整数表示的 Unicode 码位。 - `myStr.utf16` 即 `String.UTF16View`。用于获取 UTF16 编码的代码单元。 -- `myStr.utf8` 即 `String.UTF8View`。能够获取 UTF8 编码的代码单一。 +- `myStr.utf8` 即 `String.UTF8View`。能够获取 UTF8 编码的代码单元。 ![Swift 中的 CharacterView, UnicodeScalarView, UTF16View 和 UTF8View](https://rainsoft.io/content/images/2016/10/Swift-strings--3-.png) 在大多数时候开发者都在处理简单的字符串字符,而不是深入到编码或者码位这样的细节中。 -`CharacterView` 能很好的完成大多数任务:迭代字符串、字符计数、验证是否包含字符串、通过索引访问和比较操作等。 +`CharacterView` 能很好地完成大多数任务:迭代字符串、字符计数、验证是否包含字符串、通过索引访问和比较操作等。 让我们看看如何用 Swift 来完成这些任务。 @@ -52,7 +52,7 @@ print(type(of: characters))// => "CharacterView" `message.characters` 返回了 `CharacterView` 结构. -字符视图是 `Character` 结构的集合。例如我们可以这样来访问字符视图里的第一个字符: +字符视图是 `Character` 结构的集合。例如,我们可以这样来访问字符视图里的第一个字符: [Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57ff7e188ef62b25bcea2ab2) @@ -70,7 +70,7 @@ print(capitalHCharacter == firstCharacter) // => true 这个字符实例代表了单个符号 `H`。 -在 Unicode 标准中,`H` 代表 *Latin Capital letter H*,码位是 `U+0048`。 +在 Unicode 标准中,`H` 代表 *Latin Capital letter H* (拉丁文大写字母 H),码位是 `U+0048`。 让我们掠过 ASCII 看看 Swift 如何处理更复杂的符号。这些字符被渲染成单个视觉符号,但实际上是由两个或更多个 Unicode 标量](http://unicode.org/glossary/#unicode_scalar_value) 组成。严格来说这些字符被称为 **字形簇** @@ -79,7 +79,7 @@ print(capitalHCharacter == firstCharacter) // => true 让我们看看 `ç` 的字形。他可以有两种表现形式: -- 使用 `U+00E7` *LATIN SMALL LETTER C WITH CEDILLA*:被渲染为 `ç` +- 使用 `U+00E7` *LATIN SMALL LETTER C WITH CEDILLA* (拉丁文小写变音字母 C):被渲染为 `ç` - 或者使用组合字符序列:`U+0063`*LATIN SMALL LETTER C* 加上 组合标记 `U + 0327` *COMBINING CEDILLA* 组成复合字形:`c` + `◌̧` = `ç` 我们看看在第二个选项中 Swift 是如何处理它的: @@ -111,9 +111,9 @@ let multipleGraphemes: Character = "ab" // Error! 即使 `singleGrapheme` 由 3 个 Unicode 标量组成,它创建了一个字形 `ḉ`。 而 `multipleGraphemes` 则是从两个 Unicode 标量创建一个 `Character`,这将在单个 `Character` 结构中创建两个分离的字母 `a` 和 `b`,这不是被允许的操作。 -# 2. 迭代字符串中的字符 +# 2. 遍历字符串中的字符 -`CharacterView` 集合遵循了 `Sequence` 协议。这将允许在 `for-in` 循环中便利字符视图: +`CharacterView` 集合遵循了 `Sequence` 协议。这将允许在 `for-in` 循环中遍历字符视图: [Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4bc8f27a61152fe7c7410) @@ -161,7 +161,7 @@ for (index, char) in weather.characters.enumerated() { # 3. 统计字符 -只需要访问 `CharacterView` 的 `counter` 属性就可以获得字符串中字符的个数: +只需要访问 `CharacterView` 的 `count` 属性就可以获得字符串中字符的个数: [Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4bcf327a61152fe7c7413) @@ -191,11 +191,11 @@ print(drink.characters.count) // => 4 # 4. 按索引访问字符 -因为 Swift 直到它实际评估字符视图中的字形之前都不知道字符串中的字符个数。结果就造成了无法通过通过下标的方式访问字符串索引。 +因为 Swift 直到它实际评估字符视图中的字形之前都不知道字符串中的字符个数,所以无法通过下标的方式访问字符串索引。 你可以通过特殊的类型 `String.Index` 访问字符。 -如果你需要访问字符串中的第一个或者最好一个字符,字符视图结构提供了 `first` 和 `last` 属性: +如果你需要访问字符串中的第一个或者最后一个字符,字符视图结构提供了 `first` 和 `last` 属性: [Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4bd2027a61152fe7c7415) @@ -230,7 +230,7 @@ print(color[beforeEndIndex]) // => "n" ``` `color.startIndex` 是第一个字符的索引,所以 `color[startIndex]` 表示为 `g`。 -`color.endIndex` 表示**结束**位置,或者简单的说是比最后一个有效小标参数大的位置。要访问最后一个字符,你必须计算它的前一个索引:`color.index(before: color.endIndex)` +`color.endIndex` 表示**结束**位置,或者简单的说是比最后一个有效下标参数大的位置。要访问最后一个字符,你必须计算它的前一个索引:`color.index(before: color.endIndex)` 要通过偏移访问字符的位置, 在 `index(theIndex, offsetBy: theOffset)` 方法中使用 `offsetBy` 参数: @@ -459,9 +459,9 @@ if let index = weather.characters.index(of: " ") { 上面描述的许多字符串操作都是直接应用于字符串中的字符视图。 -而更方便的直接使用一个字符序列可能是更好的选择。 +如果你觉得直接对字符序列进行操作更加方便的话,那也是个不错的选择。 -比如你可以删除特定索引出的字符,或者直接删除第一个或者最好一个字符: +比如你可以删除特定索引出的字符,或者直接删除第一个或者最后一个字符: [Try in Swift sandbox](http://swiftlang.ng.bluemix.net/#/repl/57f4bea927a61152fe7c7425) @@ -531,7 +531,7 @@ print(numberOfStars) // => 2 首先要说,大家对于字符串内容持有的不同观点看起来似乎过于复杂。 -而在我看来这是一个很好的实现。字符串可以从不同的角度来例假:昨晚字形集合、UTF-8 或 UTF-16 码位和简单是 Unicode 标量。 +而在我看来这是一个很好的实现。字符串可以从不同的角度来看待:作为字形集合、UTF-8 / UTF-16 码位或者简单的 Unicode 标量。 根据你的任务来选择合适的视图。在大多数情况下,`CharacterView` 都很合适。 From fccb3e734190d7323c5c224ec48fab9e89d0e646 Mon Sep 17 00:00:00 2001 From: zhuzi Date: Tue, 11 Apr 2017 23:49:37 +0800 Subject: [PATCH 119/638] translation half finished --- TODO/secure-web-app-http-headers.md | 119 ++++++++++++++++++---------- 1 file changed, 79 insertions(+), 40 deletions(-) diff --git a/TODO/secure-web-app-http-headers.md b/TODO/secure-web-app-http-headers.md index e2f1066fd33..17aad2f24b1 100644 --- a/TODO/secure-web-app-http-headers.md +++ b/TODO/secure-web-app-http-headers.md @@ -1,29 +1,30 @@ > * 原文地址:[How To Secure Your Web App With HTTP Headers](https://www.smashingmagazine.com/2017/04/secure-web-app-http-headers/) > * 原文作者:[Hagay Lupesko](https://www.smashingmagazine.com/author/hagaylupesko/) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 译者: +> * 译者:[bambooom](https://github.com/bambooom) > * 校对者: -## [How To Secure Your Web App With HTTP Headers](https://www.smashingmagazine.com/2017/04/secure-web-app-http-headers/) ## +## 如何使用 HTTP Headers 来保护你的 Web 应用 ## -Web applications, be they thin websites or thick single-page apps, are notorious targets for cyber-attacks. In 2016, approximately [40% of data breaches](http://www.verizonenterprise.com/verizon-insights-lab/dbir/2016/) originated from attacks on web apps — the leading attack pattern. Indeed, these days, understanding cyber-security is not a luxury but rather **a necessity for web developers**, especially for developers who build consumer-facing applications. +> Web applications, be they thin websites or thick single-page apps, are notorious targets for cyber-attacks. In 2016, approximately [40% of data breaches](http://www.verizonenterprise.com/verizon-insights-lab/dbir/2016/) originated from attacks on web apps — the leading attack pattern. Indeed, these days, understanding cyber-security is not a luxury but rather **a necessity for web developers**, especially for developers who build consumer-facing applications. -HTTP response headers can be leveraged to tighten up the security of web apps, typically just by adding a few lines of code. In this article, we’ll show how web developers can use HTTP headers to build secure apps. While the code examples are for Node.js, setting HTTP response headers is supported across all major server-side-rendering platforms and is typically simple to set up. +Web 应用,无论是简单的小网页还是复杂的单页应用,众所周知都是网络攻击的目标。2016年,大约 40% 的数据泄露源自对 Web 应用的攻击,这是主要的攻击模式。事实上,现在来说,了解网络安全并不是锦上添花, 而是 Web 开发者的必需任务,特别对于构建面向消费者的产品的开发人员。 -#### Further Reading on SmashingMag: +> HTTP response headers can be leveraged to tighten up the security of web apps, typically just by adding a few lines of code. In this article, we’ll show how web developers can use HTTP headers to build secure apps. While the code examples are for Node.js, setting HTTP response headers is supported across all major server-side-rendering platforms and is typically simple to set up. -- [Facing The Challenge: Building A Responsive Web Application](https://www.smashingmagazine.com/2013/06/building-a-responsive-web-application/) -- [Getting Ready For HTTP2: A Guide For Web Designers And Developers](https://www.smashingmagazine.com/2016/02/getting-ready-for-http2/) -- [Common Security Mistakes in Web Applications](https://www.smashingmagazine.com/2010/10/common-security-mistakes-in-web-applications/) -- [Web Security: Are You Part Of The Problem?](https://www.smashingmagazine.com/2010/01/web-security-primer-are-you-part-of-the-problem/) +开发者可以利用 HTTP 响应头来加强 Web 应用程序的安全性,通常只需要添加几行代码即可。本文将结束 web 开发者如何利用 HTTP Headers 来构建安全的应用。虽然本文的示例代码是 Node.js,但是设置 HTTP 响应头基本在所有主要的服务端语言都是简单易设置的。 -### About HTTP Headers ### +### 关于 HTTP Headers ### -Technically, HTTP headers are simply fields, encoded in clear text, that are part of the HTTP request and response message header. They are designed to enable both the HTTP client and server to send and receive meta data about the connection to be established, the resource being requested, as well as the returned resource itself. +> Technically, HTTP headers are simply fields, encoded in clear text, that are part of the HTTP request and response message header. They are designed to enable both the HTTP client and server to send and receive meta data about the connection to be established, the resource being requested, as well as the returned resource itself. -Plain-text HTTP response headers can be examined easily using cURL, with the `--head` option, like so: +技术上,HTTP 头只是简单的字段,咦明文形式编码,这是 HTTP 请求和响应消息头的一部分。它们旨在使客户端和服务端都能够发送和接受有关要建立的连接元数据、所请求的资源,以及返回的资源本身的元数据。 -``` +> Plain-text HTTP response headers can be examined easily using cURL, with the `--head` option, like so: + +可以使用 cURL `--head` 选项轻松检查纯文本 HTTP 响应头,例如: + +```sh $ curl --head https://www.google.com HTTP/1.1 200 OK Date: Thu, 05 Jan 2017 08:20:29 GMT @@ -36,33 +37,51 @@ Vary: Accept-Encoding … ``` -Today, hundreds of headers are used by web apps, some standardized by the [Internet Engineering Task Force](https://www.ietf.org/) (IETF), the open organization that is behind many of the standards that power the web as we know it today, and some proprietary. HTTP headers provide a flexible and extensible mechanism that enables the rich and varying use cases found on the web today. +> Today, hundreds of headers are used by web apps, some standardized by the [Internet Engineering Task Force](https://www.ietf.org/) (IETF), the open organization that is behind many of the standards that power the web as we know it today, and some proprietary. HTTP headers provide a flexible and extensible mechanism that enables the rich and varying use cases found on the web today. + +现在,数百种响应头正在被 web 应用所使用,其中一部分由[互联网工程任务组, IETF](https://www.ietf.org/)标准化。IETF 是一个开发性组织,今天我们所熟知的许多 web 标准或专利都是由他们进行推进的。HTTP 头提供了一种灵活可扩展的机制,造就了现今的网络各种丰富多变的用例。 + +### 机密资源禁用缓存 ### + +> Caching is a valuable and effective technique for optimizing performance in client-server architectures, and HTTP, which leverages caching extensively, is no exception. However, in cases where the cached resource is confidential, caching can lead to vulnerabilities — and must be avoided. As an example, consider a web app that renders and caches a page with sensitive information and is being used on a shared PC. Anyone can view confidential information rendered by that web app simply by visiting the browser’s cache, or sometimes even as easily as clicking the browser’s “back” button! + +缓存是优化客户端-服务端架构性能中有效的技术,广泛利用缓存的 HTTP 也不例外。但是,在缓存的资源是保密的情况下,缓存可能导致漏斗,所以必须避免。假设一个 web 应用对含有敏感信息的网页进行缓存,并且是在一台公用的 PC 上使用。任何人可以通过访问浏览器的缓存看到这个 web 应用上的敏感信息,甚至有时仅仅通过点击浏览器的返回按钮就可以看到。 -### Disabling Caching Of Confidential Resources ### +> The IETF’s [RFC 7234](https://tools.ietf.org/html/rfc7234), which defines HTTP caching, specifies the default behavior of HTTP clients, both browsers and intermediary Internet proxies, to *always* cache responses to HTTP `GET` requests — unless specified otherwise. While this enables HTTP to boost performance and reduce network congestion, it could also expose end users to theft of personal information, as mentioned above. The good news is that the HTTP specification also defines a pretty simple way to instruct clients not to cache a given response, through the use of — you guessed it! — HTTP response headers. -Caching is a valuable and effective technique for optimizing performance in client-server architectures, and HTTP, which leverages caching extensively, is no exception. However, in cases where the cached resource is confidential, caching can lead to vulnerabilities — and must be avoided. As an example, consider a web app that renders and caches a page with sensitive information and is being used on a shared PC. Anyone can view confidential information rendered by that web app simply by visiting the browser’s cache, or sometimes even as easily as clicking the browser’s “back” button! +IETF 的 [RFC 7234](https://tools.ietf.org/html/rfc7234) 上定义了 HTTP 缓存,指定 HTTP 客户端(浏览器以及网络代理)的默认行为,也就是始终缓存对 HTTP GET 请求的相应,除非另行指定。虽然这样可以使 HTTP 提升性能减少网络拥塞,但如上所述,它也有可能使终端用户个人信息被盗。好消息是,HTTP 规范还廷议了一种非常简单的方式来指示客户端对特定响应不进行缓存,通过使用 —— 对,你猜到了 —— HTTP 响应头。 -The IETF’s [RFC 7234](https://tools.ietf.org/html/rfc7234), which defines HTTP caching, specifies the default behavior of HTTP clients, both browsers and intermediary Internet proxies, to *always* cache responses to HTTP `GET` requests — unless specified otherwise. While this enables HTTP to boost performance and reduce network congestion, it could also expose end users to theft of personal information, as mentioned above. The good news is that the HTTP specification also defines a pretty simple way to instruct clients not to cache a given response, through the use of — you guessed it! — HTTP response headers. +> There are three headers to return when you are returning sensitive information and would like to disable caching by HTTP clients: -There are three headers to return when you are returning sensitive information and would like to disable caching by HTTP clients: +当你返回敏感信息并希望禁用 HTTP 客户端的缓存时,有三个头可以返回: - `Cache-Control` -This response header, introduced in HTTP 1.1, may contain one or more directives, each carrying a specific caching semantic, and instructing HTTP clients and proxies on how to treat the response being annotated by the header. My recommendation is to format the header as follows: `cache-control: no-cache, no-store, must-revalidate`. These three directives pretty much instruct clients and intermediary proxies not to use a previously cached response, not to store the response, and that even if the response is somehow cached, the cache must be revalidated on the origin server. +> This response header, introduced in HTTP 1.1, may contain one or more directives, each carrying a specific caching semantic, and instructing HTTP clients and proxies on how to treat the response being annotated by the header. My recommendation is to format the header as follows: `cache-control: no-cache, no-store, must-revalidate`. These three directives pretty much instruct clients and intermediary proxies not to use a previously cached response, not to store the response, and that even if the response is somehow cached, the cache must be revalidated on the origin server. + +从 HTTP 1.1 引入的此响应头可能包含一个或多个指令,每个指令带有特定的缓存语义,指示 HTTP 客户端和代理如何处理有此响应头注释的响应。我推荐如下指定响应头,`cache-control: no-cache, no-store, must-revalidate`。这三个指令基本上可以指示客户端和中间代理不可使用之前缓存的响应,不可存储响应,甚至就算响应被缓存,也必须从源服务器上重新验证。 - `Pragma: no-cache` - -For backwards-compatibility with HTTP 1.0, you will want to include this header as well. Some HTTP clients, especially intermediary proxies, still might not fully support HTTP 1.1 and so will not correctly handle the `Cache-Control` header mentioned above. Use `Pragma: no-cache` to ensure that these older clients do not cache your response. + +> For backwards-compatibility with HTTP 1.0, you will want to include this header as well. Some HTTP clients, especially intermediary proxies, still might not fully support HTTP 1.1 and so will not correctly handle the `Cache-Control` header mentioned above. Use `Pragma: no-cache` to ensure that these older clients do not cache your response. + +为了与 HTTP 1.0 的向后兼容性,你还需要包含此响应头。有部分客户端,特别是中间代理,可能仍然没有完全支持 HTTP 1.1,所以不能正确处理前面提到的 `Cache-Control` 响应头,所以使用 `Pragma: no-cache` 确保较旧的客户端不缓存你的响应。 - `Expires: -1` -This header specifies a timestamp after which the response is considered stale. By specifying `-1`, instead of an actual future time, you ensure that clients immediately treat this response as stale and avoid caching. +> This header specifies a timestamp after which the response is considered stale. By specifying `-1`, instead of an actual future time, you ensure that clients immediately treat this response as stale and avoid caching. -Note that, while disabling caching enhances the security of your web app and helps to protect confidential information, is does come at the price of a performance hit. Make sure to disable caching only for resources that actually require confidentiality and not just for any response rendered by your server! For a deeper dive into best practices for caching web resources, I highly recommend reading [Jake Archibald’s post](https://jakearchibald.com/2016/caching-best-practices/) on the subject. +此标头指定了该响应过时的时间戳。如果不指定为未来某个真实时间而指定为 `-1`,可以保证客户端立即将此响应视为过时并避免缓存。 -Here’s how you would program these headers in Node.js: +> Note that, while disabling caching enhances the security of your web app and helps to protect confidential information, is does come at the price of a performance hit. Make sure to disable caching only for resources that actually require confidentiality and not just for any response rendered by your server! For a deeper dive into best practices for caching web resources, I highly recommend reading [Jake Archibald’s post](https://jakearchibald.com/2016/caching-best-practices/) on the subject. -``` +需要注意的是,禁用缓存提高安全性及保护机密资源的同时,也的确会带来性能上的折损。所以确保仅对实际需要保密性的资源禁用缓存,而不是对任何服务器的响应禁用。想要更深入了解 web 资源缓存的最佳实践,我推荐阅读 [Jake Archibald 的文章](https://jakearchibald.com/2016/caching-best-practices/)。 + +> Here’s how you would program these headers in Node.js: + +下面是 Node.js 中设置响应头的示例代码: + +```javascript function requestHandler(req, res) { res.setHeader('Cache-Control','no-cache,no-store,max-age=0,must-revalidate'); res.setHeader('Pragma','no-cache'); @@ -70,37 +89,58 @@ function requestHandler(req, res) { } ``` -### Enforcing HTTPS ### +### 强制 HTTPS ### -Today, the importance of HTTPS is widely recognized by the tech community. More and more web apps configure secured endpoints and are redirecting unsecure traffic to secured endpoints (i.e. HTTP to HTTPS redirects). Unfortunately, end users have yet to fully comprehend the importance of HTTPS, and this lack of comprehension exposes them to various man-in-the-middle (MitM) attacks. The typical user navigates to a web app without paying much attention to the protocol being used, be it secure (HTTPS) or unsecure (HTTP). Moreover, many users will just click past browser warnings when their browser presents a certificate error or warning! +> Today, the importance of HTTPS is widely recognized by the tech community. More and more web apps configure secured endpoints and are redirecting unsecure traffic to secured endpoints (i.e. HTTP to HTTPS redirects). Unfortunately, end users have yet to fully comprehend the importance of HTTPS, and this lack of comprehension exposes them to various man-in-the-middle (MitM) attacks. The typical user navigates to a web app without paying much attention to the protocol being used, be it secure (HTTPS) or unsecure (HTTP). Moreover, many users will just click past browser warnings when their browser presents a certificate error or warning! -The importance of interacting with web apps over a valid HTTPS connection cannot be overstated: An unsecure connection exposes the user to various attacks, which could lead to cookie theft or worse. As an example, it is not very difficult for an attacker to spoof network frames within a public Wi-Fi network and to extract the session cookies of users who are not using HTTPS. To make things even worse, even users interacting with a web app over a secured connection may be exposed to downgrade attacks, which try to force the connection to be downgraded to an unsecure connection, thus exposing the user to MitM attacks. +今天,HTTPS 的重要性已经得到了科技界的广泛认可。越来越多的 web 应用配置了安全端点,并将不安全网路重定向到安全端点(即 HTTP 重定向至 HTTPS)。不幸的是,终端用户还未完全理解 HTTPS 的重要性,这种缺乏理解使他们面临着各种中间人攻击(MitM)。典型的用户访问到一个 web 应用时,并不会注意到正在使用的网络协议是安全的(HTTPS)还是不安全的(HTTP)。 -How can we help users avoid these attacks and better enforce the usage of HTTPS? Enter the HTTP Strict Transport Security (HSTS) header. Put simply, HSTS makes sure all communications with the origin host are using HTTPS. Specified in [RFC 6797](https://tools.ietf.org/html/rfc6797), HSTS enables a web app to instruct browsers to allow *only* HTTPS connections to the origin host, to internally redirect all unsecure traffic to secured connections, and to automatically upgrade all unsecure resource requests to be secure. +> The importance of interacting with web apps over a valid HTTPS connection cannot be overstated: An unsecure connection exposes the user to various attacks, which could lead to cookie theft or worse. As an example, it is not very difficult for an attacker to spoof network frames within a public Wi-Fi network and to extract the session cookies of users who are not using HTTPS. To make things even worse, even users interacting with a web app over a secured connection may be exposed to downgrade attacks, which try to force the connection to be downgraded to an unsecure connection, thus exposing the user to MitM attacks. -HSTS directives include the following: +通过有效的 HTTPS 连接与 web 应用进行交互的重要性怎么说都不算夸大:不安全的连接将用户暴露给各种攻击,这可能导致 cookie 被盗甚至更糟。举个例子,攻击者可以轻易在公共 Wi-Fi 网络下骗过网络帧并提起不使用 HTTPS 的用户的会话 cookie。更糟的情况是,即使用户通过安全连接与 web 永盈进行交互也可能遭受降级攻击,这种攻击试图强制将连接降级到不安全的连接,从而是用户收到中间人攻击。 + +> How can we help users avoid these attacks and better enforce the usage of HTTPS? Enter the HTTP Strict Transport Security (HSTS) header. Put simply, HSTS makes sure all communications with the origin host are using HTTPS. Specified in [RFC 6797](https://tools.ietf.org/html/rfc6797), HSTS enables a web app to instruct browsers to allow *only* HTTPS connections to the origin host, to internally redirect all unsecure traffic to secured connections, and to automatically upgrade all unsecure resource requests to be secure. + +我们如何帮助用户避免这些攻击,并更好地实施 HTTPS 的使用呢?使用 HTTP 严格传输安全头(HSTS)。简单来说,HSTS 确保与源主机间的所有通信都使用 HTTPS。[RFC 6797](https://tools.ietf.org/html/rfc6797) 中说明了,HSTS可以使 web 应用程序指示浏览器仅允许与源主机之间的 HTTPS 连接,将所有不安全的连接内部重定向到安全连接,并自动将所有不安全的资源请求升级为安全请求。 + +> HSTS directives include the following: + +HSTS 的指令如下: - `max-age=` -This instructs the browser to cache this header, for this domain, for the specified number of seconds. This can ensure tightened security for a long duration! +>This instructs the browser to cache this header, for this domain, for the specified number of seconds. This can ensure tightened security for a long duration! + +此项指示浏览器对此域缓存此响应头指定的秒数。这样可以保证长时间的加固安全。 - `includeSubDomains` -This instructs the browser to apply HSTS for all subdomains of the current domain. This can be useful to cover all current and future subdomains you may have. +>This instructs the browser to apply HSTS for all subdomains of the current domain. This can be useful to cover all current and future subdomains you may have. + +此项指示浏览器对当前域的所有子域应用 HSTS,这可以用于覆盖你可能有的所有当前和未来的子域。 - `preload` -This is a powerful directive that forces browsers to *always* load your web app securely, even on the first hit, before the response is even received! This works by hardcoding a list of HSTS preload-enabled domains into the browser’s code. To enable the preloading feature, you need to register your domain with [HSTS Preload List Submission](https://hstspreload.org), a website maintained by Google’s Chrome team. Once registered, the domain will be prebuilt into supporting browsers to always enforce HSTS. The preload directive within the HTTP response header is used to confirm registration, indicating that the web app and domain owner are indeed interested in being on the preload list. +> This is a powerful directive that forces browsers to *always* load your web app securely, even on the first hit, before the response is even received! This works by hardcoding a list of HSTS preload-enabled domains into the browser’s code. To enable the preloading feature, you need to register your domain with [HSTS Preload List Submission](https://hstspreload.org), a website maintained by Google’s Chrome team. Once registered, the domain will be prebuilt into supporting browsers to always enforce HSTS. The preload directive within the HTTP response header is used to confirm registration, indicating that the web app and domain owner are indeed interested in being on the preload list. -A word of caution: using the `preload` directive also means it cannot be easily undone, and carries an update lead time of months! While preload certainly improves your app’s security, it also means you need to be fully confident your app can support HTTPS-only! +这是一个强大的指令,强制浏览器始终安全加载你的 web 应用程序,即使是第一次收到响应之前加载!这是通过将启用 HSTS 预加载域的列表硬编码到浏览器的代码中实现的。要启用预加载功能,你需要在 Google Chrome 团队维护的网站 [HSTS 预加载列表提交](https://hstspreload.org)注册你的域。 -My recommendation is to use `Strict-Transport-Security: max-age=31536000; includeSubDomains;` which instructs the browser to enforce a valid HTTPS connection to the origin host and to all subdomains for a year. If you are confident that your app can handle HTTPS-only, I would also recommend adding the `preload` directive, in which case don’t forget to register your website on the preload list as well, as noted above! +> A word of caution: using the `preload` directive also means it cannot be easily undone, and carries an update lead time of months! While preload certainly improves your app’s security, it also means you need to be fully confident your app can support HTTPS-only! -Here’s what implementing HSTS looks like in Node.js: +注意谨慎使用 `preload`,因为这意味着它不能轻易撤销,并带有几个月前的更新。虽然预加载肯定会应用程序的安全性,但也意味着你需要充分确信你的应用程序可以支持仅 HTTPS! -``` -functionrequestHandler(req, res){ - res.setHeader('Strict-Transport-Security','max-age=31536000; includeSubDomains; preload');} +> My recommendation is to use `Strict-Transport-Security: max-age=31536000; includeSubDomains;` which instructs the browser to enforce a valid HTTPS connection to the origin host and to all subdomains for a year. If you are confident that your app can handle HTTPS-only, I would also recommend adding the `preload` directive, in which case don’t forget to register your website on the preload list as well, as noted above! + +我推荐的用法是 `Strict-Transport-Security: max-age=31536000; includeSubDomains;`,这样指示了浏览器强制通过 HTTPS 连接到源主机并且有效期为一年。如果你对你的 app 处理 HTTPS 限定很有信心,我也推荐加上 `preload` 指令,当然别忘记去前面提到的预加载列表注册你的网站。 + +> Here’s what implementing HSTS looks like in Node.js: + +以下是在 Nodes.js 中实现 HSTS 的方法: + +```javascript +function requestHandler(req, res){ + res.setHeader('Strict-Transport-Security','max-age=31536000; includeSubDomains; preload'); +} ``` ### Enabling XSS Filtering ### @@ -248,7 +288,6 @@ Remember that for the web to be truly awesome and engaging, it has to be secure. *Front page image credits: [Pexels.com](https://www.pexels.com/photo/coffee-writing-computer-blogging-34600/).* - --- > [掘金翻译计划](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)、[React](https://github.com/xitu/gold-miner#react)、[前端](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)。 From 535f0275fb15aac1dd4b5def4cd27bfc3362d470 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=B9=E5=8F=B7=E4=B8=89?= Date: Wed, 12 Apr 2017 10:15:25 +0800 Subject: [PATCH 120/638] Update css-is-fine-its-just-really-hard.md --- TODO/css-is-fine-its-just-really-hard.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO/css-is-fine-its-just-really-hard.md b/TODO/css-is-fine-its-just-really-hard.md index 423250fcfb8..20cb8558bcd 100644 --- a/TODO/css-is-fine-its-just-really-hard.md +++ b/TODO/css-is-fine-its-just-really-hard.md @@ -1,5 +1,5 @@ > * 原文地址:[CSS is Fine, It’s Just Really Hard](https://medium.com/@jdan/css-is-fine-its-just-really-hard-638da7a3dce0) -> * 原文作者:[Jordan Scales](https://medium.com/@jdan) +> * 原文作者:该文章已获原作者 [Jordan Scales](https://medium.com/@jdan) 授权 > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 译者: [ZhangFe](https://github.com/ZhangFe) > * 校对者:[bambooom](https://github.com/bambooom),[gy134340](https://github.com/gy134340) From 88f2c4e5960ea4f8351d8095b3f71418d5346a7e Mon Sep 17 00:00:00 2001 From: ivyxuan Date: Wed, 12 Apr 2017 10:45:39 +0800 Subject: [PATCH 121/638] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO/the-details-that-matter.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TODO/the-details-that-matter.md b/TODO/the-details-that-matter.md index b4ccfb5d9f8..69114a7588b 100644 --- a/TODO/the-details-that-matter.md +++ b/TODO/the-details-that-matter.md @@ -80,7 +80,7 @@ Airbnb 的提示听起来像人说的话而且语气还很随和。 -**像“出错啦”这种警告对所有的用户都会造成困扰,而且还会惹恼专家级用户。**但是,一个精心设计过的错误信息,会顿时化失望为欣喜。所以,把报错变得人性化、不用技术性的语言并且适合你的用户群体。 +**像“出错啦”这种警告对所有的用户都会造成困扰,而且还会惹恼专家级用户。** 但是,一个精心设计过的错误信息,会顿时化失望为欣喜。所以,把报错变得人性化、不用技术性的语言并且适合你的用户群体。 @@ -100,7 +100,7 @@ Airbnb 的提示听起来像人说的话而且语气还很随和。 #### 让用户界面更容易理解 #### -**杂乱的堆砌是很糟糕的一件事情。**在界面上杂乱堆砌元素会给用户带来过多的信息:每一个被添加的按钮、图片和文字都会让界面显得更加复杂。如果你不想你的设计有任何刻意的留白的话,下面这个例子就能很明白的告诉你,有太多东西一起吸引你的注意力是多么可怕的事情。 +**杂乱的堆砌是很糟糕的一件事情。** 在界面上杂乱堆砌元素会给用户带来过多的信息:每一个被添加的按钮、图片和文字都会让界面显得更加复杂。如果你不想你的设计有任何刻意的留白的话,下面这个例子就能很明白的告诉你,有太多东西一起吸引你的注意力是多么可怕的事情。 From 6e460df0aceb6017d65043c6bdcf1042bbce31bc Mon Sep 17 00:00:00 2001 From: reid Date: Wed, 12 Apr 2017 11:55:31 +0800 Subject: [PATCH 122/638] =?UTF-8?q?=E5=AE=8C=E6=88=90=E7=BF=BB=E8=AF=91?= =?UTF-8?q?=E5=88=9D=E7=A8=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...gher-order-functions-composing-software.md | 89 +++++++++---------- 1 file changed, 44 insertions(+), 45 deletions(-) diff --git a/TODO/higher-order-functions-composing-software.md b/TODO/higher-order-functions-composing-software.md index aa432f1141e..67336f3fd47 100644 --- a/TODO/higher-order-functions-composing-software.md +++ b/TODO/higher-order-functions-composing-software.md @@ -1,22 +1,22 @@ -> * 原文地址:[Higher Order Functions (Composing Software)(part 4)](https://medium.com/javascript-scene/higher-order-functions-composing-software-5365cf2cbe99) -> * 原文作者:[Eric Elliott](https://medium.com/@_ericelliott?source=post_header_lockup) -> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 译者: -> * 校对者: +> * 原文地址:[Higher Order Functions (Composing Software)(part 4)](https://medium.com/javascript-scene/higher-order-functions-composing-software-5365cf2cbe99) +> * 原文作者:[Eric Elliott](https://medium.com/@_ericelliott?source=post_header_lockup) +> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) +> * 译者:[reid3290](https://github.com/reid3290) +> * 校对者: -# Higher Order Functions (Composing Software) # +# 高阶函数(软件编写)(第四部分) # -Smoke Art Cubes to Smoke — MattysFlicks — (CC BY 2.0) -> Note: This is part of the “Composing Software” series on learning functional programming and compositional software techniques in JavaScript ES6+ from the ground up. Stay tuned. There’s a lot more of this to come! -> [< Previous](https://medium.com/javascript-scene/a-functional-programmers-introduction-to-javascript-composing-software-d670d14ede30#.gof8dsqh9) | [<< Start over at Part 1](https://medium.com/javascript-scene/the-rise-and-fall-and-rise-of-functional-programming-composable-software-c2d91b424c8c#.2dfd6n6qe) | [Next >](https://medium.com/javascript-scene/reduce-composing-software-fe22f0c39a1d) +Smoke Art Cubes to Smoke — MattysFlicks — (CC BY 2.0)(译注:该图是用 PS 将烟雾处理成方块状后得到的效果,参见 [flickr](https://www.flickr.com/photos/68397968@N07/11432696204)。) +> 注意:这是“软件编写”系列文章的第四部分,该系列主要阐述如何在 JavaScript ES6+ 中从零开始学习函数式编程和组合化软件(compositional software)技术(译注:关于软件可组合性的概念,参见维基百科 [Composability](https://en.wikipedia.org/wiki/Composability))。后续还有更多精彩内容,敬请期待! +> [< 上一篇](https://medium.com/javascript-scene/a-functional-programmers-introduction-to-javascript-composing-software-d670d14ede30#.gof8dsqh9) | [<< 第一篇](https://medium.com/javascript-scene/the-rise-and-fall-and-rise-of-functional-programming-composable-software-c2d91b424c8c#.2dfd6n6qe) | [下一篇 >](https://medium.com/javascript-scene/reduce-composing-software-fe22f0c39a1d) -A **higher order function** is a function that takes a function as an argument, or returns a function. Higher order function is in contrast to first order functions, which don’t take a function as an argument or return a function as output. +**高阶函数**是一种接收一个函数作为输入或输出一个函数的函数(译注:参见维基百科[高阶函数](https://zh.wikipedia.org/wiki/%E9%AB%98%E9%98%B6%E5%87%BD%E6%95%B0)),这是和一阶函数截然不同的。 -Earlier we saw examples of `.map()` and `.filter()`. Both of them take a function as an argument. They're both higher order functions. +之前我们看到的 `.map()` 和 `.filter()` 都是高阶函数 —— 它们都接受一个函数作为参数, -Let’s look at an example of a first-order function which filters all the 4-letter words from a list of words: +先来看个一阶函数的例子,该函数会将单词数组中 4 个字母的单词过滤掉: ``` const censor = words => { @@ -32,7 +32,7 @@ censor(['oops', 'gasp', 'shout', 'sun']); // [ 'shout', 'sun' ] ``` -Now what if we want to select all the words that begin with ‘s’? We could create another function: +如果又要选择出所有以 's' 开头的单词呢?可以再定义一个函数: ``` const startsWithS = words => { @@ -48,29 +48,29 @@ startsWithS(['oops', 'gasp', 'shout', 'sun']); // [ 'shout', 'sun' ] ``` -You may already be recognizing a lot of repeated code. There’s a pattern forming here that could be abstracted into a more generalized solution. These two functions have a whole lot in common. They both iterate over a list and filter it on a given condition. +显然可以看出这里面有很多重复的代码,这两个函数的主体是相同的 —— 都是遍历一个数组并根据给定的条件进行过滤。这便形成了一种特定的模式,可以从中抽象出更为通用的解决方案。 -Both the iteration and the filtering seem like they’re begging to be abstracted so they can be shared and reused to build all sorts of similar functions. After all, selecting things from lists of things is a very common task. +不难看出, “遍历”和“过滤”都是亟待抽象出来的,以便分享和复用到其他所有类似的函数中去。毕竟,从数组中选取某些特定元素是很常见的需求。 -Luckily for us, JavaScript has first class functions. What does that mean? Just like numbers, strings, or objects, functions can be: +幸运的是,函数是 JavaScript 中的一等公民,就像数字、字符串和对象一样,函数可以: -- Assigned as an identifier (variable) value -- Assigned to object property values -- Passed as arguments -- Returned from functions +- 像变量一样赋值给其他变量 +- 作为对象的属性值 +- 作为参数进行传递 +- 作为函数的返回值 -Basically, we can use functions just like any other bits of data in our programs, and that makes abstraction a lot easier. For instance, we can create a function that abstracts the process of iterating over a list an accumulating a return value by passing in a function that handles *the bits that are different.* We’ll call that function the *reducer:* +函数基本上可以像其他任何数据类型一样被使用,这点使得“抽象”容易了许多。例如,可以定义一种函数,将遍历数组并累计出一个返回值的过程抽象出来,该函数接收一个函数作为参数来决定具体的**累计**过程,不妨将此函数称为 **reducer**: ``` const reduce = (reducer, initial, arr) => { - // shared stuff + // 共享的 let acc = initial; for (let i = 0, length = arr.length; i < length; i++) { - // unique stuff in reducer() call + // 独特的 acc = reducer(acc, arr[i]); - // more shared stuff + // 又是共享的 } return acc; }; @@ -78,14 +78,14 @@ const reduce = (reducer, initial, arr) => { reduce((acc, curr) => acc + curr, 0, [1,2,3]); // 6 ``` -This `reduce()` implementation takes a reducer function, an initial value for the accumulator, and an array of data to iterate over. For each item in the array, the reducer is called, passing it the accumulator and the current array element. The return value is assigned to the accumulator. When it's finished applying the reducer to all of the values in the list, the accumulated value is returned. +该 `reduce()` 接受 3 个参数:一个 reducer 函数、一个累计的初始值和一个用于遍历的数组。对数组中的每个元素都会调用 reducer,传入累计器和当前数组元素,返回值又会赋给累计器。对数组中的所有元素都执行过 reducer 之后,返回最终的累计结果。 -In the usage example, we call reduce and pass it the function, `(acc, curr) => acc + curr`, which takes the accumulator and the current value in the list and returns a new accumulated value. Next we pass an initial value, `0`, and finally, the data to iterate over. +在用例中,调用 `reduce` 并传给它 3 个参数:`reducer` 函数、初始值 0 以及需要遍历的数组。其中 `reducer` 函数以累计器和当前数组元素为参数,返回累计后的结果。 -With the iteration and value accumulation abstracted, now we can implement a more generalized `filter()` function: +如此将遍历和累计的过程抽象出来之后,便可实现更为通用的 `filter()` 函数: ``` -const filter = ( + const filter = ( fn, arr ) => reduce((acc, curr) => fn(curr) ? acc.concat([curr]) : @@ -93,11 +93,11 @@ const filter = ( ); ``` -In the `filter()` function, everything is shared except the `fn()` function that gets passed in as an argument. That `fn()`argument is called a predicate. A **predicate** is a function that returns a boolean value. +在此 `filter()` 函数中,除了以参数形式传进来的 `fn()` 函数以外,所有代码都是可复用的。其中 `fn()` 参数被称为**断言(predicate)** —— 返回一个布尔值的函数。 -We call `fn()` with the current value, and if the `fn(curr)` test returns `true`, we concat the `curr` value to the accumulator array. Otherwise, we just return the current accumulator value. +将当前值传给 `fn()`,如果 `fn(curr)` 返回 `true`,则将 `curr` 添加到结果数组中并返回之;否则,直接返回当前数组。 -Now we can implement `censor()` with `filter()` to filter out 4-letter words: +现在便可借助 `filter()` 函数来实现过滤 4 字母单词的 `censor()` 函数: ``` const censor = words => filter( @@ -106,20 +106,20 @@ const censor = words => filter( ); ``` -Wow! With all the common stuff abstracted out, `censor()` is a tiny function. +喔!将所有公共代码抽象出来之后,`censor()` 函数便十分简洁了。 -And so is `startsWithS()`: +`startsWithS()` 也是如此: ``` -const startsWithS = words => filter( + const startsWithS = words => filter( word => word.startsWith('s'), words ); ``` -If you’re paying attention, you probably know that JavaScript has already done this abstraction work for us. We have the `Array.prototype` methods, `.reduce()` and `.filter()` and `.map()` and a few more for good measure. + 你若稍加留意便会发现 JavaScript 其实已经为我们做了这些抽象,即 `Array.prototype` 的相关方法,例如 `.reduce()`、`.filter()`、`.map()` 等等。 -Higher order functions are also commonly used to abstract how to operate on different data types. For instance, `.filter()` doesn't have to operate on arrays of strings. It could just as easily filter numbers, because you can pass in a function that knows how to deal with a different data type. Remember the `highpass()` example? + 高阶函数也常常被用于对不同数据类型的操作进行抽象。例如,`.filter()` 函数不一定非得作用于字符串数组。只需传入一个能够处理不同数据类型的函数,`.filter()` 便能过滤数字了。还记得 `highpass` 的例子吗? ``` const highpass = cutoff => n => n >= cutoff; @@ -127,22 +127,21 @@ const gt3 = highpass(3); [1, 2, 3, 4].filter(gt3); // [3, 4]; ``` -In other words, you can use higher order functions to make a function polymorphic. As you can see, higher order functions can be a whole lot more reusable and versatile than their first order cousins. Generally speaking, you’ll use higher order functions in combination with very simple first order functions in your real application code. +换言之,高阶函数可以用来实现函数的多态性。如你所见,相较于一阶函数而言,高阶函数的复用性和通用性非常好。一般来讲,在实际编码中会组合使用高阶函数和一些非常简单的一阶函数。 -[**Continue to “Reduce” >**](https://medium.com/javascript-scene/reduce-composing-software-fe22f0c39a1d) +[**再续 “Reduce” >**](https://medium.com/javascript-scene/reduce-composing-software-fe22f0c39a1d) -### Next Steps ### +### 接下来 ### -Want to learn more about functional programming in JavaScript? +想学习更多 JavaScript 函数式编程吗? -[Learn JavaScript with Eric Elliott](http://ericelliottjs.com/product/lifetime-access-pass/). If you’re not a member, you’re missing out! +[跟着 Eric Elliott 学 Javacript](http://ericelliottjs.com/product/lifetime-access-pass/),机不可失时不再来! -[ -](https://ericelliottjs.com/product/lifetime-access-pass/) +[](https://ericelliottjs.com/product/lifetime-access-pass/) -***Eric Elliott*** is the author of [*“Programming JavaScript Applications”*](http://pjabook.com) (O’Reilly), and *[*“Learn JavaScript with Eric Elliott”*](http://ericelliottjs.com/product/lifetime-access-pass/). He has contributed to software experiences for **Adobe Systems, Zumba Fitness, The Wall Street Journal, ESPN, BBC, and top recording artists including Usher, Frank Ocean, Metallica**, and many more. +**Eric Elliott** 是 [**“编写 JavaScript 应用”**](http://pjabook.com) (O’Reilly) 以及 [**“跟着 Eric Elliott 学 Javascript”**](http://ericelliottjs.com/product/lifetime-access-pass/) 两书的作者。他为许多公司和组织作过贡献,例如 **Adobe Systems**、**Zumba Fitness**、**The Wall Street Journal**、**ESPN** 和 **BBC**等 , 也是很多机构的顶级艺术家,包括但不限于 **Usher**、**Frank Ocean** 以及 **Metallica**。 -*He spends most of his time in the San Francisco Bay Area with the most beautiful woman in the world.* +大多数时间,他都在 San Francisco Bay Area,同这世上最美丽的女子在一起。 --- From c38df1b848dcd5e272debf8e17d3d0d99d7c3452 Mon Sep 17 00:00:00 2001 From: lsvih Date: Wed, 12 Apr 2017 12:57:17 +0800 Subject: [PATCH 123/638] =?UTF-8?q?=E4=BA=8C=E6=A0=A1=E6=84=8F=E8=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO/webpack-and-rollup-the-same-but-different.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/TODO/webpack-and-rollup-the-same-but-different.md b/TODO/webpack-and-rollup-the-same-but-different.md index b80a7886547..dd15131dc7b 100644 --- a/TODO/webpack-and-rollup-the-same-but-different.md +++ b/TODO/webpack-and-rollup-the-same-but-different.md @@ -2,7 +2,7 @@ > * 原文作者:[Rich Harris](https://medium.com/@Rich_Harris?source=post_header_lockup) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 译者:[lsvih](https://github.com/lsvih) -> * 校对者: +> * 校对者:[avocadowang](https://github.com/avocadowang),[Aladdin-ADD](https://github.com/Aladdin-ADD) # 同中有异的 Webpack 与 Rollup # @@ -18,14 +18,14 @@ Webpack 由 [Tobias Koppers](https://medium.com/@sokra) 在 2012 年创建,用于解决当时的工具不能处理的问题:构建复杂的单页应用(SPA)。尤其是它的两个特点改变了一切: -1. **代码分割**可以将你的 app 分割成许多个容易管理的分块,这些分块能够在用户使用你的 app 时按需加载。这意味着你的用户可以有更快的交互体验。因为访问那些网站必须要等待整个应用都被下载并解析完成。当然,你**也可以**自己手动去进行代码分割,但是……总之,祝你好运。 +1. **代码分割**可以将你的 app 分割成许多个容易管理的分块,这些分块能够在用户使用你的 app 时按需加载。这意味着你的用户可以有更快的交互体验。因为访问那些没有使用代码分割的应用时,必须要等待整个应用都被下载并解析完成。当然,你**也可以**自己手动去进行代码分割,但是……总之,祝你好运。 2. **静态资源**的导入:图片、CSS 等静态资源可以直接导入到你的 app 中,就和其它的模块、节点一样能够进行依赖管理。因此,我们再也不用小心翼翼地将各个静态文件放在特定的文件夹中,然后再去用脚本给文件 URL 加上哈希串了。Webpack 已经帮你完成了这一切。 而 Rollup 的开发理念则不同:它利用 ES2015 模块的巧妙设计,尽可能高效地构建精简且易分发的 JavaScript 库。而其它的模块打包器(包括 Webpack在内)都是通过将模块分别封装进函数中,然将这些函数通过能在浏览器中实现的 `require` 方法打包,最后依次处理这些函数。在你需要实现按需加载的时候,这种做法非常的方便,但是这样做引入了很多无关代码,比较浪费资源。当[你有很多模块要打包的时候,这种情况会变得更糟糕](https://nolanlawson.com/2016/08/15/the-cost-of-small-modules/)。 ES2015 模块则启用了一种不同的实现方法,Rollup 用的也就是这种方法。所有代码都将被放置在同一个地方,并且会在一起进行处理。因此得到的最终代码相较而言会更加的精简,运行起来自然也就更快。你可以[点击这儿亲自试试 Rollup 交互式解释器(REPL)](https://rollupjs.org/repl)。 -但这儿也存在一些需要权衡的点:代码分割是一个很棘手的问题,而 Rollup 并不能做到这一点。同样的,Rollup 也不支持模块热替换(HMR)。而且对于打算使用 Rollup 的人来说,还有一个最大的痛点:它通过[插件](https://github.com/rollup/rollup-plugin-commonjs)处理大多数 CommonJS 文件的时候,一些代码将无法被翻译回 ES2015。而与之相反,你可以把这一切的事全部放心交给 Webpack 去处理。 +但这儿也存在一些需要权衡的点:代码分割是一个很棘手的问题,而 Rollup 并不能做到这一点。同样的,Rollup 也不支持模块热替换(HMR)。而且对于打算使用 Rollup 的人来说,还有一个最大的痛点:它通过[插件](https://github.com/rollup/rollup-plugin-commonjs)处理大多数 CommonJS 文件的时候,一些代码将无法被翻译为 ES2015。而与之相反,你可以把这一切的事全部放心交给 Webpack 去处理。 ### 那么我到底应该选用哪一个呢? ### From 60d29b02c6319a3063e1f9630a741a3f84261cd2 Mon Sep 17 00:00:00 2001 From: lsvih Date: Wed, 12 Apr 2017 12:57:44 +0800 Subject: [PATCH 124/638] =?UTF-8?q?=E4=BA=8C=E6=A0=A1=E6=84=8F=E8=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From 6cc14ad5fa43bbcfee01296502a37a1ec1fe999b Mon Sep 17 00:00:00 2001 From: reid Date: Wed, 12 Apr 2017 15:23:34 +0800 Subject: [PATCH 125/638] fix a typo --- ...bpack-bits-getting-the-most-out-of-the-commonschunkplugin.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO/webpack-bits-getting-the-most-out-of-the-commonschunkplugin.md b/TODO/webpack-bits-getting-the-most-out-of-the-commonschunkplugin.md index 20692bd6b00..228984136a1 100644 --- a/TODO/webpack-bits-getting-the-most-out-of-the-commonschunkplugin.md +++ b/TODO/webpack-bits-getting-the-most-out-of-the-commonschunkplugin.md @@ -10,7 +10,7 @@ webpack 核心团队隔三差五地就会在 Twitter 上作一些寓教于乐的 ![Markdown](http://i4.buimg.com/1949/614a949156a09f9e.png) -这次的“游戏规则”很简单:安装 `webpacl-bundle-analyzer`,生成一张包含所有 bundles 信息的酷炫图片分享给我,然后 webpack 团队会帮忙指出任何潜在的问题。 +这次的“游戏规则”很简单:安装 `webpack-bundle-analyzer`,生成一张包含所有 bundles 信息的酷炫图片分享给我,然后 webpack 团队会帮忙指出任何潜在的问题。 ### 我们发现了什么? ### From ed2b2bf92d3d20a03c69f824b063742d52ea65c8 Mon Sep 17 00:00:00 2001 From: DeepMissea <398752853@qq.com> Date: Wed, 12 Apr 2017 19:15:35 +0800 Subject: [PATCH 126/638] =?UTF-8?q?=E6=96=9C=20=E2=99=82=20=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO/mvvmc-with-swift.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/TODO/mvvmc-with-swift.md b/TODO/mvvmc-with-swift.md index eb77db7fd87..a4119cbe373 100644 --- a/TODO/mvvmc-with-swift.md +++ b/TODO/mvvmc-with-swift.md @@ -454,15 +454,15 @@ MVVM-C 有很多优点,可以提高应用程序的质量。你应该注意使 # FAQ -***MVVM-C 有什么限制吗?*** +**MVVM-C 有什么限制吗?** 是的,当然有。如果你正做一个复杂的项目,你可能会遇到一些边缘案例,MVVM-C 可能无法使用,或者在一些小功能上使用过度。如果你开始使用 MVVM-C,并不意味着你必须在每个地方都强制的使用它,你应该始终选择更适合你需求的架构。 -***我能用 RxSwift 同时使用函数式和命令式编程吗?*** +**我能用 RxSwift 同时使用函数式和命令式编程吗?** 是的,你可以。但是我建议你在遗留的代码中保持命令式的方式,而在新的实现里使用函数式编程,这样你可以利用 RxSwift 强大的优势。如果你使用 RxSwift 仅仅为了 UI 绑定,你可以轻松使用命令式编写程序,而只用函数响应式编程来设置绑定。 -***我可以在企业项目中使用 RxSwift 吗?*** +**我可以在企业项目中使用 RxSwift 吗?** 这取决于你要开新项目,还是要维护旧代码。在有遗留代码的项目中,你可能无法使用 RxSwift,因为你需要重构很多的类。如果你有时间和资源来做,我建议你新开一项目一点一点的做,否则还是尝试其他的方法来解决 UI 绑定的问题。 From 38d15c20727c6208b97ab4ac9b5ae6b2c3447667 Mon Sep 17 00:00:00 2001 From: zhuzi Date: Wed, 12 Apr 2017 22:24:57 +0800 Subject: [PATCH 127/638] fix Punctuation --- TODO/beyond-browser-web-desktop-apps.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/TODO/beyond-browser-web-desktop-apps.md b/TODO/beyond-browser-web-desktop-apps.md index 1899ee8f1d9..f40c59cd68e 100644 --- a/TODO/beyond-browser-web-desktop-apps.md +++ b/TODO/beyond-browser-web-desktop-apps.md @@ -16,7 +16,7 @@ ### 为什么? -首先,为什么会有人开发桌面应用?任何现有的 web 应用(不同于网站,如果你认为它们是不同的)都可能适合变成一个桌面应用。你可以围绕任何可以从与用户系统集成中获益的 web 应用构建桌面应用;例如本地通知,开机启动,与文件的交互等。有些用户单纯更喜欢在自己的电脑中永久保存一些 app,无论是否联网都可以访问。 +首先,为什么会有人开发桌面应用?任何现有的 web 应用(不同于网站,如果你认为它们是不同的)都可能适合变成一个桌面应用。你可以围绕任何可以从与用户系统集成中获益的 web 应用构建桌面应用;例如本地通知、开机启动、与文件的交互等。有些用户单纯更喜欢在自己的电脑中永久保存一些 app,无论是否联网都可以访问。 也许你有个想法,但只能用作桌面应用,有些事情只是在 web 应用中不可能实现(至少还有一点,但更多的是这一点)。你可能想要为公司内部创建一个独立的功能性应用程序,而不需要任何人安装除了你的 app 之外的任何内容(因为内置 Node.js )。也许你有个有关 Mac 应用商店的想法,也许只是你的一个个人兴趣的小项目。 @@ -26,23 +26,23 @@ ### NW.js -桌面应用已经有很长一段时间了,我知道你没有很多时间,所以我们跳过一些历史,从2011年的上海开始。来自 Intel 开源技术中心的 Roger Wang 开发了 node-webkit,一个概念验证的 Node.js 模块,这个模块可以让用户创建一个 WebKit 内核的浏览器窗口并直接在 ` ``` -As you may realize, this URL will make the browser run the injected script and send the user’s cookies, potentially including confidential session cookies, to evil.com! +> As you may realize, this URL will make the browser run the injected script and send the user’s cookies, potentially including confidential session cookies, to evil.com! + +你可能意识到了,这个 URL 会让浏览器执行注入的脚本,并发送用户的 cookies,极有可能包含机密的会话 cookie,至 evil.com。 -To help protect users against reflective XSS attacks, some browsers have implemented protection mechanisms. These mechanisms try to identify these attacks by looking for matching code patterns in the HTTP request and response. Internet Explorer was the first browser to introduce such a mechanism with its XSS filter, introduced in Internet Explorer 8 back in 2008, and WebKit later introduced XSS Auditor, available today in Chrome and Safari. (Firefox has no similar mechanism built in, but users can use add-ons to gain this functionality.) These various protection mechanisms are not perfect: They may fail to detect a real XSS attack (a false negative), and in other cases may block legitimate code (a false positive). Due to the latter, browsers allow users to disable the XSS filter via the settings. Unfortunately, this is typically a global setting, which turns off this security feature completely for all web apps loaded by the browser. +> To help protect users against reflective XSS attacks, some browsers have implemented protection mechanisms. These mechanisms try to identify these attacks by looking for matching code patterns in the HTTP request and response. Internet Explorer was the first browser to introduce such a mechanism with its XSS filter, introduced in Internet Explorer 8 back in 2008, and WebKit later introduced XSS Auditor, available today in Chrome and Safari. (Firefox has no similar mechanism built in, but users can use add-ons to gain this functionality.) These various protection mechanisms are not perfect: They may fail to detect a real XSS attack (a false negative), and in other cases may block legitimate code (a false positive). Due to the latter, browsers allow users to disable the XSS filter via the settings. Unfortunately, this is typically a global setting, which turns off this security feature completely for all web apps loaded by the browser. -Luckily, there is a way for a web app to override this configuration and ensure that the XSS filter is turned on for the web app being loaded by the browser. This is done via the `X-XSS-Protection` header. This header, supported by Internet Explorer (from version 8), Edge, Chrome and Safari, instructs the browser to turn on or off the browser’s built-in protection mechanism and to override the browser’s local configuration. +为了帮助保护用户抵抗反射型 XSS 攻击,有些浏览器实施了保护机制。这些保护机制尝试通过在 HTTP 请求和响应中寻找匹配的代码模式来辨识这些攻击。Internet Explorer 是第一个推出这种机制的,在 2008 年的 IE 8 中引入了 XSS 过滤器的机制,而 WebKit 后来推出了 XSS 审计,现今在 Chrome 和 Safari 上可用。(Firefox 没有内置类似的机制,但是用户可以使用插件来获得此功能)。这些保护机制并不完美,它们可能无法检测到真正的 XSS 攻击(漏报),在其他情况可能会阻止合法代码(误判)。由于后一种情况的出现,浏览器允许用户可设置禁用 XSS 过滤功能。不幸的是,这通常是一个全局设置,这会完全关闭所有浏览器加载的 web 应用程序的安全功能。 -`X-XSS-Protection` directives include these: +> Luckily, there is a way for a web app to override this configuration and ensure that the XSS filter is turned on for the web app being loaded by the browser. This is done via the `X-XSS-Protection` header. This header, supported by Internet Explorer (from version 8), Edge, Chrome and Safari, instructs the browser to turn on or off the browser’s built-in protection mechanism and to override the browser’s local configuration. -- `1` or `0` +幸运的是,有方法可以让 web 应用程序覆盖此配置,并确保浏览器加载的 web 应用已打开 XSS 过滤器。这是通过设定 `X-XSS-Protection` 响应头来达到的。此响应头支持 Internet Explorer (8以上)、Edge、Chrome 和 Safar,指示浏览器打开或关闭浏览器内置的保护机制,及覆盖浏览器的本地配置。 -This enables or disables the filter. +`X-XSS-Protection` 指令包括: + +- `1` 或者 `0` + +使用或禁用 CSS 过滤器。 - `mode=block` -This instructs the browser to prevent the entire page from rendering when an XSS attack is detected. +> This instructs the browser to prevent the entire page from rendering when an XSS attack is detected. + +当检测到 XSS 攻击时,这会指示浏览器不渲染整个页面。 -I recommend always turning on the XSS filter, as well as block mode, to maximize user protection. Such a response header looks like this: +> I recommend always turning on the XSS filter, as well as block mode, to maximize user protection. Such a response header looks like this: + +我推荐永远打开 XSS 过滤器以及 block 模式,以求最大化保护用户。这样的响应头应该是这样的: ``` X-XSS-Protection: 1; mode=block ``` -Here’s how you would configure this response header in Node.js: +以下是在 Node.js 中配置此响应头的方法: -``` +```javascript functionrequestHandler(req, res){ res.setHeader('X-XSS-Protection','1;mode=block');} ``` From d9d798c63b6ebe5e5b001cdc6323f7d10a4f6440 Mon Sep 17 00:00:00 2001 From: lsvih Date: Wed, 12 Apr 2017 23:53:12 +0800 Subject: [PATCH 139/638] Update swift-lazy-initialization-with-closures.md --- ...swift-lazy-initialization-with-closures.md | 230 ++++++++++-------- 1 file changed, 130 insertions(+), 100 deletions(-) diff --git a/TODO/swift-lazy-initialization-with-closures.md b/TODO/swift-lazy-initialization-with-closures.md index 051ab41f287..41885f1449f 100644 --- a/TODO/swift-lazy-initialization-with-closures.md +++ b/TODO/swift-lazy-initialization-with-closures.md @@ -1,23 +1,23 @@ -> * 原文地址:[Swift Lazy Initialization with Closures](https://blog.bobthedeveloper.io/swift-lazy-initialization-with-closures-a9ef6f6312c) -> * 原文作者:[Bob Lee](https://blog.bobthedeveloper.io/@bobthedev) -> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 译者: +> * 原文地址:[Swift Lazy Initialization with Closures][1] +> * 原文作者:[Bob Lee][2] +> * 译文出自:[掘金翻译计划][3] +> * 译者:[lsvih][4] > * 校对者: -# Swift Lazy Initialization with Closures # +# 在 Swift 中使用闭包实现懒加载 -## Learn how to create objects with modularity and readability ## +## 学习如何兼顾模块化与可读性来创建对象 ![](https://cdn-images-1.medium.com/max/2000/1*KNmIy5QAOeokXPW86TtVyA.png) -Magic Keyboard 2 and Magic Mouse 2 +(图为苹果的 Magic Keyboard 2 与 Magic Mouse 2) -*Welcome my lovely readers. Good to see you here today. For those who are new, I’m Bob. Just for real quick, if you wish to be on my mailing list, you can sign up* [*here*](https://boblee.typeform.com/to/oR9Nt2) *and get more value for your learning with iOS development :)* +*亲爱的读者你们好!我是 Bob,很高兴能在这篇文章中与你们相遇!如你想加入我的邮件列表,获取更多学习 iOS 开发的文章,请点击*[*这儿*][5]*注册,很快就能完成的哦 :)* -### Motivation ### +### 动机 -In the beginning of my iOS journey, I followed tutorials on YouTube. I saw a few using something like below to create UI objects. +在我刚开始学习 iOS 开发的时候,我在 YouTube 上找了一个教程。我发现这个教程有时候会用下面这种方式来创建 UI 对象: ``` let makeBox: UIView = { @@ -26,42 +26,42 @@ let makeBox: UIView = { }() ``` -As a learner, I copied the practice and used it. One day, however, one of my readers asked me, “why do you add `{}` and why does `()` exist at the end? Is it a computed property?” I could not answer. I was a zombie. +作为一个初学者,我自然而然地复制并使用了这个例子。直到有一天,我的一个读者问我:“为什么你要加上`{}`呢?最后为什么要加上一对`()`呢?这是一个计算属性吗?”我哑口无言,因为我自己也不知道答案。 -I wrote this tutorial for my younger self. Yet, some may find it useful. +因此,我为过去年轻的自己写下了这份教程。说不定还能帮上其他人的忙。 -### Objectives ### +### 目标 -There are three objectives. First, understand how to initialize an object using the unconventional way as shown above. Second, learn when to use `lazy var` in Swift. Last, join my mailing list. +这篇教程有一下三个目标:第一,了解如何像前面的代码一样,非常规地创建对象;第二,知道编在写 Swfit 代码时,什么时候该使用 `lazy var`;第三,快加入我的邮件列表呀。 -#### Prerequisites #### +#### 预备知识 -To fully enjoy the ride with me, I highly recommend you to be familiar with the topics below. +为了让你能轻松愉快地和我一起完成这篇教程,我强烈推荐你先了解下面这几个概念。 -1. [*Closures*](https://blog.bobthedeveloper.io/no-fear-closure-in-swift-3-with-bob-72a10577c564) -2. [*Capture List and retention cycle [weak self]*](https://blog.bobthedeveloper.io/swift-retention-cycle-in-closures-and-delegate-836c469ef128) -3. *Descent Object Oriented Programming* +1. [*闭包*][6] +2. [*捕获列表与保留周期 \[weak self]*][7] +3. *面向对象程序设计* -### Create UI Components ### +### 创建 UI 组件 -Before I explain the unconventional method above, let’s look into your past. In order to create a button in Swift, you probably have done something like this, +在我介绍“非常规”方法之前,让我们先复习一下“常规”方法。在 Swift 中,如果你要创建一个按钮,你应该会这么做: ``` -// Determine Size +// 设定尺寸 let buttonSize = CGRect(x: 0, y: 0, width: 100, height: 100) -// Create Instance +// 创建控件 let bobButton = UIButton(frame: buttonSize) bobButton.backgroundColor = .black bobButton.titleLabel?.text = "Bob" bobButton.titleLabel?.textColor = .white ``` -This is *Okay.* +这样做**没问题**。 -Assume, you have to create three other buttons, you probably have to copy the code above and then change the name from `bobButton` to `bobbyButton`. +假设现在你要创建另外三个按钮,你很可能会把上面的代码复制,然后把变量名从 `bobButton` 改成 `bobbyButton`。 -It’s quite tedious. +这未免也太单调了吧。 ``` // New Button @@ -71,13 +71,13 @@ bobbyButton.titleLabel?.text = "Bob" bobbyButton.titleLabel?.textColor = .white ``` -To make things just a bit easier, you may +为了方便,你可以: ![](https://cdn-images-1.medium.com/max/800/1*oDIPy0i4YzUnKVR4XYI4kg.gif) -This works too with the keyboard shortcut: ctrl-cmd-e +使用快捷键:ctrl-cmd-e 来完成这个工作。 -If you don’t wish to repeat yourself, you may create a function instead. +如果你不想做重复的工作,你也可以创建一个函数。 ``` func createButton(enterTitle: String) -> UIButton { @@ -86,27 +86,26 @@ func createButton(enterTitle: String) -> UIButton { button.titleLabel?.text = enterTitle return button } - createButton(enterTitle: "Yoyo") // 👍 ``` -However, in iOS development, it is rare that custom buttons look similar. Therefore, a function may require a lot more parameters including background color, title, border radius, shadow, and so on. You function may end up looking like, +然而,在 iOS 开发中,很少会看到一堆一模一样的按钮。因此,这个函数需要接受更多的参数,如背景颜色、文字、圆角尺寸、阴影等等。你的函数最后可能会变成这样: ``` func createButton(title: String, borderWidth: Double, backgrounColor, ...) -> Button ``` -The code above is not ideal even if you add default parameters to the function. It decreases readability. Therefore, it’s better to stay with the tedious method above. +但是,上面的代码并不理想。即使你为这个函数加上了默认参数,它的可读性很差。因此,比起这个方法,我们还是采用上面那个”单调“的方法为妙。 -But, is there any way we can make it less tedious and more organized? Of course. We’ve looked into your past — It’s time to step up and look into your future. +到底有没有办法让我们既不那么无聊,还能让代码更有条理呢?当然咯。我们现在只是复习你过去的做法——是时候更上一层楼,展望你未来的做法了。 -### Introducing the Unconventional Way ### +### 介绍”非常规“方法 -Before we create UI components with the unconventional way, let’s first answer the initial questions my reader asked. What does `{}` mean, and is it a `computed property`? +在我们使用”非常规“方法创建 UI 组件之前,让我们先回答一下最开始那个读者的问题。`{}`是什么意思,它是一个`计算属性`吗? -*Nope, it’s just a* ***closure block***. +*当然不是,它只是一个* ***闭包***。 -First, let’s demonstrate how to create an object using a closure. We will design a struct called `Human`. +首先,让我来示范一下如何用闭包来创建一个对象。我们设计一个名为`Human`的结构: ``` struct Human { @@ -116,7 +115,7 @@ struct Human { } ``` -Now, this is how you create an object with a closure +现在,让你看看怎么用闭包创建对象: ``` let createBob = { () -> Human in @@ -127,11 +126,11 @@ let createBob = { () -> Human in let babyBob = createBob() // "Born 1996" ``` -*If the syntax above doesn’t look familiar to you, you may stop reading now, and go to* [*Fear No Closure with Bob*](https://blog.bobthedeveloper.io/no-fear-closure-in-swift-3-with-bob-72a10577c564) *, and bring some bullets.* +*如果你不熟悉这段语法,请先停止阅读这篇文章,去看看* [*Fear No Closure with Bob*][8] *充充电吧。* -Just to explain, `createBob` is a closure whose type is `() -> Human`. You’ve created an instance called, `babyBob` by calling `createBob()` . +解释一下,`createBob` 是一个类型为 `()-> Human` 的闭包。你已经通过调用 `createBob()` 创建好了一个 `babyBob` 实例。 -However, you had to create two constants: `createBob` and `babyBob`. What if you want to do everything in a single statement? Here you go. +然而,这样做你创建了两个常量:`createBob` 与 `babyBob`。如何把所有的东西都放在一个声明中呢?请看: ``` let bobby = { () -> Human in @@ -140,11 +139,11 @@ let bobby = { () -> Human in }() ``` -Now, the closure block executes itself through adding `()` at the end and `bobby` now has a `Human` object attached. Pretty good stuff. +现在,这个闭包通过在最后加上 `()` 执行了自己,`bobby` 现在附上了一个 `Human` 对象。干的漂亮! -**You’ve learned how to initialize an object with a closure block.** +**现在你已经学会了使用闭包来创建一个对象** -Now, let’s apply to creating an UI object which should be similar to the example right above. +让我们应用这个方法,模仿上面的例子来创建一个 UI 对象吧。 ``` let bobView = { () -> UIView in @@ -153,8 +152,7 @@ let bobView = { () -> UIView in return view }() ``` - -Great, we can make it shorter. In fact, we don’t need to specify the type of the closure block. Instead, all we have to do is specify the type of the instance, `bobView`, for example. +很好,我们还能让它更简洁。实际上,我们不需要为闭包指定类型,我们只需要指定 `bobView` 实例的类型就够了。例如: ``` let bobbyView: **UIView** = { @@ -164,17 +162,17 @@ let bobbyView: **UIView** = { }() ``` -Swift is able to infer that the closure block is `() -> UIView` based on the keyword, `return`. +Swift 能够通过关键字 `return` 推导出这个闭包的类型是 `() -> UIView`。 -Now, take a look. The example right above should look identical to the “unconventional way” I feared. +现在看看,上面的例子已经和我之前害怕的“非常规方式”一样了。 -### Benefits of Init with Closures ### +### 使用闭包创建的好处 -We discussed the tediousness of creating objects and the problem that arises from using a function. In your head, you must be thinking, “why should I use a closure block instead?” +我们已经讨论了:直接创建对象是很单调无聊的,使用函数构造是会出现问题的。现在你可能会想“为什么我非得用闭包来创建?” -#### Easy to Duplicate #### +#### 重复起来更容易 -I don’t like to use Storyboard, I love copy and pasting UI objects. In fact, I’ve a “library” of code in my computer. Let us assume that there is a button as shown below in the library. +我不喜欢用 Storyboard,我比较喜欢复制粘贴 UI 对象。实际上,在我电脑里有一个“库”的代码。假设库里有个按钮,代码如下: ``` let myButton: UIButton = { @@ -188,14 +186,14 @@ return button }() ``` -All I have to do is copy the entire lines, and then just change the name of `myButton` to `newButton` for the usage. Had I not used the closure method, I probably had to change the name of `button` to `newButton` 7–8 times. We could use the Xcode shortcut above, but why not make it just simpler. +我只需要把它整个复制,然后把名字从 `myButton` 改成 `newButtom` 就行了。在我用闭包之前,我得重复地把 `myButton` 改成 `newButtom` ,甚至要改上七八遍。我们虽然可以用 Xcode 的快捷键,但为啥不使用闭包,让这件事更简单呢? -#### Look Cleaner #### +#### 看起来更干净 -Since objects are grouped together, it feels cleaner based on my eyes. Let’s compare +由于对象对象会自己编好组,在我看来它更加的干净。让我们对比一下: ``` -// Init with Closure +// 使用闭包创建 let leftCornerButton: UIButton = { let button = UIButton(frame: buttonSize) button.backgroundColor = .black @@ -220,7 +218,7 @@ return button vs ``` -// Init With Fingers +// 使用我的手指创建.. let leftCornerButton = UIButton(frame: buttonSize) leftCornerButton.backgroundColor = .black leftCornerButton.titleLabel?.text = "Button" @@ -236,81 +234,81 @@ rightCornerButton.layer.cornerRadius = rightCornerButton.layer.masksToBounds = true ``` -Although creating an object with the closure add a couple lines more, I feel less overwhelmed since I only have to add attributes to `button` rather than `rightCornerButton` or `leftCornerButton`. +虽然使用闭包创建对象甚至还多出了一行,但是比起要在 `rightCornerButton` 或者 `leftCornerButton` 后面狂加属性,我还是更喜欢在 `button` 后面加属性。 -*In fact, if the name of a button gets more descriptive, often times it requires fewer lines to create an object with a closure block.* +*实际上如果按钮的命名特别详细时,用闭包创建对象还可以少几行。* -**You’ve accomplished the first objective. Congratulations** +**恭喜你,你已经完成了我们的第一个目标** -### Lazy Init Application ### +### 懒加载的应用 -You’ve come a long way. It’s time to meet the second objective of this tutorial. +辛苦了!现在让我们来看看这个教程的第二个目标把。 -You might have seen something like this below +你可能看过与下面类似的代码: ``` class IntenseMathProblem { - lazyvar complexNumber: Int = { - // imagine it requires a lot of CPU + lazy var complexNumber: Int = { + // 请想象这儿要耗费很多CPU资源 1 * 1 }() } ``` -What `lazy` allows you to do is, the `complexNumber` property will be only calculated when you try to access it. For example, +`lazy` 的作用是,让 `complexNumber` 属性只有在你试图访问它的时候才会被计算。例如: ``` let problem = IntenseMathProblem -problem() // No value for complexNumber +problem() // 此时complexNumber没有值 ``` -Currently, there is no value for `complexNumber`. However, once you access the property, +没错,现在 `complexNumber` 没有值。然而,一旦你访问这个属性: ``` -problem().complexNumber // Now returns 1 +problem().complexNumber // 现在回返回1 ``` -The `lazy var` is often used to sort database and fetch data from any backend services because you definitely don’t want to calculate and sort everything when you create an object. +`lazy var` 经常用于数据库排序或者从后端取数据,因为你并不想在创建对象的时候就把所有东西都计算、排序。 -*In fact, your phone will crash since the object is super bloated and the RAM can’t handle.* +*实际上,由于对象太大了导致 RAM 撑不住,你的手机就会崩溃。* -### Application ### +### 应用 -Below is just an application of `lazy var`. +以下是 `lazy var` 的应用: -#### Sorting #### +#### 排序 ``` class SortManager { lazy var sortNumberFromDatabase: [Int] = { - // Sorting logic + // 排序逻辑 return [1, 2, 3, 4] }() } ``` -#### Image Compression #### +#### 图片压缩 ``` class CompressionManager { lazy var compressedImage: UIImage = { let image = UIImage() - // Compress the image - // Logic + // 压缩图片的 + // 逻辑 return image }() } ``` -### Rules with `Lazy` ### +### `Lazy`的一些规定 -1. You can’t use `lazy` with `let` since there is no initial value, and it is attained later when it is accessed. -2. You can’t use it with a `computed property` since computed property is always recalculated (requires CPU) when you modify any of the variables that has a relationship with the `lazy` property. -3. `Lazy` is only valid for members of a struct or class +1. 你不能把 `lazy` 和 `let` 一起用,因为用 `lazy` 时没有初值,只有当被访问时才会获得值。 +2. 你不能把它和 `计算属性` 一起用,因为在你修改任何与 `lazy` 的计算属性有关的变量时,计算属性都会被重新计算(耗费 CPU 资源)。 +3. `Lazy` 只能是结构或类的成员。 -### Does Lazy Capture? ### +### Lazy 能被捕获吗? -So, if you’ve read the previous article on [Retention Cycle in Closures and Delegate](https://blog.bobthedeveloper.io/swift-retention-cycle-in-closures-and-delegate-836c469ef128) , you might wonder. Let’s test it out. Create a class called `BobGreet`. It has two properties: `name` whose type is `String` and `greeting` whose type is also `String` but initialized with a closure block. +如果你读过我的前一篇文章[《Swift 闭包和代理中的保留周期》][9],你就会明白这个问题。让我们试一试吧。创建一个名叫 `BobGreet` 的类,它有两个属性:一个是类型为 `String` 的 `name`,一个是类型为 `String` 但是使用闭包创建的 `greeting`。 ``` class BobGreet { @@ -320,43 +318,75 @@ class BobGreet { }() deinit { - print("I'm gone, bruh 🙆‍")} + print("I'm gone, bruh 🙆")} } } ``` -The closure block *might* have a strong reference to `BobGuest` but let’s attempt to deallocate. +闭包**可能**对 `BobGuest` 有强引用,让我们尝试着 deallocate 它。 ``` var bobGreet: BobGreet? = BobClass() bobGreet?.greeting -bobClass = nil // I'm gone, bruh 🙆‍ +bobClass = nil // I'm gone, bruh 🙆 ``` -No need to worry about `[unowned self]` The closure block does not have a reference to the object. Instead, it just copies `self` within the closure block. If you are confused by the previous statement, feel free to read [Swift Capture Lists](https://blog.bobthedeveloper.io/swift-capture-list-in-closures-e28282c71b95) to learn more. 👍 +不用担心 `[unowned self]`,闭包并没有对对象存在引用。相反,它仅仅是在闭包内复制了 `self`。如果你对前面的代码声明有疑问,放松一会儿,读读 [Swift Capture Lists][10] 来了解更多这方面的知识。👍 -### Last Remarks ### +### 最后的唠叨 -I learned a quite a bit while preparing for this tutorial. I hope you did as well. I’d appreciate your genuine fat ❤️. But, there is just one more. As the last objective, if you wish to on my mailing list and receive greater value from me, you can sign up right [**here**](https://boblee.typeform.com/to/oR9Nt2) . +我在准备这篇教程的过程中也学到了很多,希望你也一样。感谢你们的热情❤️!不过这篇文章还剩一点:我的最后一个目标。如果你希望加入我的邮件列表以获得更多有价值的信息的话,你可以点 [**这里**][11]注册。 -As you can see by the cover photo, I recently bought Magic Keyboard and Mouse. They are pretty good and increase my productivity a lot. You can get the mouse [here](http://amzn.to/2noHxgl) or the keyboard [here](http://amzn.to/2noHxgl). I never regret despite the price. 😓 +正如封面照片所示,我最近买了 Magic Keyboard 和 Magic Mouse。它们超级棒,帮我提升了很多的效率。你可以在 [这儿][12]买鼠标,在 [这儿][13]买键盘。我才不会因为它们的价格心疼呢。😓 -> [Source Code](https://github.com/bobthedev/Blog_Lazy_Init_with_Closures) +> [本文的源码][14] -### Swift Conference I Will Join ### +### 我将要参加 Swift 讨论会 -I will be joining my first conference @[SwiftAveir](https://twitter.com/SwiftAveiro) o from June 1–2. A friend of mine, [Joao](https://twitter.com/NSMyself) , is helping organize the conference, so I’m super excited. You can learn more about the event [here](http://swiftaveiro.xyz) ! +我将在 6 月 1 日至 6 月 2 日 参加我有生以来的第一次讨论会 @[SwiftAveir][15], 我的朋友 [Joao][16]协助组织了这次会议,所以我非常 excited。你可以点[这儿][17]了解这件事 的详情! -#### Recommended Articles #### +#### 文章推荐 -> Intro to Functional Programming ([Blog](https://blog.bobthedeveloper.io/intro-to-swift-functional-programming-with-bob-9c503ca14f13)) +> 函数式编程简介 ([Blog][18]) -> My Favorite Xcode Shortcuts ([Blog](https://blog.bobthedeveloper.io/intro-to-swift-functional-programming-with-bob-9c503ca14f13) ) +> 我最爱的 XCode 快捷键 ([Blog][19] ) -### Bob the Developer ### +### 关于我 -I’m an iOS instructor from Seoul, 🇰🇷. Feel free to get to know me on [Instagram](https://instagram.com/bobthedev) . I post regular updates on [Facebook Page](https://facebook.com/bobthedeveloper) and 🖨 on Sat 8pm EST. +我是一名来自首尔的 iOS 课程教师,你可以在 [Instagram][20] 上了解我。我会经常在 [Facebook Page][21] 投稿,投稿时间一般在北京时间上午9点(Sat 8pm EST)。 --- -> [掘金翻译计划](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)、[React](https://github.com/xitu/gold-miner#react)、[前端](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)。 +> [掘金翻译计划][22] 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金][23] 上的英文分享文章。内容覆盖 [Android][24]、[iOS][25]、[React][26]、[前端][27]、[后端][28]、[产品][29]、[设计][30] 等领域,想要查看更多优质译文请持续关注 [掘金翻译计划][31]。 + +[1]: https://blog.bobthedeveloper.io/swift-lazy-initialization-with-closures-a9ef6f6312c +[2]: https://blog.bobthedeveloper.io/@bobthedev +[3]: https://github.com/xitu/gold-miner +[4]: https://github.com/lsvih +[5]: https://boblee.typeform.com/to/oR9Nt2 +[6]: https://blog.bobthedeveloper.io/no-fear-closure-in-swift-3-with-bob-72a10577c564 +[7]: https://juejin.im/post/58e4ac5d44d904006d2a9a19 +[8]: https://blog.bobthedeveloper.io/no-fear-closure-in-swift-3-with-bob-72a10577c564 +[9]: https://juejin.im/post/58e4ac5d44d904006d2a9a19 +[10]: https://blog.bobthedeveloper.io/swift-capture-list-in-closures-e28282c71b95 +[11]: https://boblee.typeform.com/to/oR9Nt2 +[12]: http://amzn.to/2noHxgl +[13]: http://amzn.to/2noHxgl +[14]: https://github.com/bobthedev/Blog_Lazy_Init_with_Closures +[15]: https://twitter.com/SwiftAveiro +[16]: https://twitter.com/NSMyself +[17]: http://swiftaveiro.xyz +[18]: https://blog.bobthedeveloper.io/intro-to-swift-functional-programming-with-bob-9c503ca14f13 +[19]: https://blog.bobthedeveloper.io/intro-to-swift-functional-programming-with-bob-9c503ca14f13 +[20]: https://instagram.com/bobthedev +[21]: https://facebook.com/bobthedeveloper +[22]: https://github.com/xitu/gold-miner +[23]: https://juejin.im +[24]: https://github.com/xitu/gold-miner#android +[25]: https://github.com/xitu/gold-miner#ios +[26]: https://github.com/xitu/gold-miner#react +[27]: https://github.com/xitu/gold-miner#%E5%89%8D%E7%AB%AF +[28]: https://github.com/xitu/gold-miner#%E5%90%8E%E7%AB%AF +[29]: https://github.com/xitu/gold-miner#%E4%BA%A7%E5%93%81 +[30]: https://github.com/xitu/gold-miner#%E8%AE%BE%E8%AE%A1 +[31]: https://github.com/xitu/gold-miner From 69bca85e75f4b99b33c193c21f577db93622ee8b Mon Sep 17 00:00:00 2001 From: gy134340 Date: Thu, 13 Apr 2017 00:20:55 +0800 Subject: [PATCH 140/638] =?UTF-8?q?=E6=A0=A1=E5=AF=B9=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...amming-in-javascript-composing-software.md | 74 +++++++++---------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/TODO/why-learn-functional-programming-in-javascript-composing-software.md b/TODO/why-learn-functional-programming-in-javascript-composing-software.md index 3203cee2ce5..c260bf55297 100644 --- a/TODO/why-learn-functional-programming-in-javascript-composing-software.md +++ b/TODO/why-learn-functional-programming-in-javascript-composing-software.md @@ -4,29 +4,29 @@ > * 译者: > * 校对者: -# 为什么用 JavaScript 学习函数式编程?(组成化软件)(第二部分) +# 为什么用 JavaScript 学习函数式编程?(软件构建)(第二部分) 烟雾的方块艺术 —MattysFlicks —(CC BY 2.0) -> 注意:这是从基础学习函数式编程和使用 JavaScript ES6+ 组成软件的第一部分。保持关注,接下来还有很多! +> 注意:这是从基础学习函数式编程和使用 JavaScript ES6+ 撰写软件的第二部分。保持关注,接下来还有很多! > [从第一部分开始](https://medium.com/javascript-scene/the-rise-and-fall-and-rise-of-functional-programming-composable-software-c2d91b424c8c#.2dfd6n6qe) | [接下来的 >](https://medium.com/javascript-scene/a-functional-programmers-introduction-to-javascript-composing-software-d670d14ede30#.2e4youss2) -忘了你认为知道的关于 JavaScript 的一切,用初学者的眼光去看待它。为了帮助你做到这一点,我们将会从头复习一下 JavaScript 的基础,就像你与其尚未谋面一样。如果你是初学者,那你就很幸运了。最终从零开始探索 ES6 和函数式编程!希望所有的概念都被解释清楚 — 但不要太依赖于此。 +忘掉你认为知道的关于 JavaScript 的一切,用初学者的眼光去看待它。为了帮助你做到这一点,我们将会从头复习一下 JavaScript 的基础,就像你与其尚未谋面一样。如果你是初学者,那你就很幸运了。最终从零开始探索 ES6 和函数式编程!希望所有的概念都被解释清楚 — 但不要太依赖于此。 如果你是已经熟悉 JavaScript 或者纯函数式语言的老开发者了,也许你会认为 JavaScript 是探索函数式编程有趣的选择。把这些想法放在一边,用更开放的思想接触它,你会发现 JavaScript 编程更高层次的东西。一些你从来不知道的东西。 -由于这个被称为“组成式软件”,同时函数式编程是明显的组成软件的方法(使用函数组合,高阶函数等等),你也许想知道为什么我不用 Haskell, ClojureScript,或者 Elm,而是 JavaScript。 +由于这个被称为“组合式软件”,同时函数式编程是明显的构建软件的方法(使用函数组合,高阶函数等等),你也许想知道为什么我不用 Haskell、ClojureScript,或者 Elm,而是 JavaScript。 JavaScript 有函数式编程所需要的最重要的特性: -1. **一级函数:**使用函数作为数据值的能力:用函数传参,返回函数,用函数做变量和对象属性。这个属性允许更高级别的函数,使局部应用、柯里化和组合成为可能。 -2. **匿名函数和简介的 lambda 语法:**`x => x * 2` 是 JavaScript 中有效的函数表达式。简介的 lambda 语法使得它更好的跟高阶函数合作。 -3. **闭包:**闭包是一个有着自己独立作用域的捆绑函数。闭包在函数被创建时被创建。当一个函数在另一个函数内部被创建,它可以访问外部函数的变量,即使在外部函数退出后。闭包时使局部应用或者固定的参数。固定的参数时绑定在返回函数的作用域范围内的参数。在 `add2(1)(2)` 中,`1` 是 `add2(1)` 返回的函数中的固定参数。 +1. **一级公民函数:**使用函数作为数据值的能力:用函数传参,返回函数,用函数做变量和对象属性。这个属性允许更高级别的函数,使偏函数应用、柯里化和组合成为可能。 +2. **匿名函数和简洁的 lambda 语法:**`x => x * 2` 是 JavaScript 中有效的函数表达式。简洁的 lambda 语法使得高阶函数变的简单。 +3. **闭包:**闭包是一个有着自己独立作用域的捆绑函数。闭包在函数被创建时被创建。当一个函数在另一个函数内部被创建,它可以访问外部函数的变量,即使在外部函数退出后。通过闭包偏函数应用可以获取内部固定参数。固定的参数时绑定在返回函数的作用域范围内的参数。在 `add2(1)(2)` 中,`1` 是 `add2(1)` 返回的函数中的固定参数。 ### JavaScript 缺少了什么 -JavaScript 是多范式语言,意味着它支持多种风格的编程。其他被 JavaScript 支持的风格包括过程式的(命令式)编程(比如 C),把函数看作可以被重复调用和组织的子程序指令;面向对象编程,对象 — 而不是函数 — 作为初始构造块;当然,还有函数式编程。多范式编程语言的劣性在于命令式和面向对象往往意味着所有东西都是可变的。 +JavaScript 是多范式语言,意味着它支持多种风格的编程。其他被 JavaScript 支持的风格包括过程式(命令式)编程(比如 C),把函数看作可以被重复调用和组织的子程序指令;面向对象编程,对象— 而不是函数— 作为初始构造块;当然,还有函数式编程。多范式编程语言的劣性在于命令式和面向对象往往意味着所有东西都是可变的。 可变性指的是数据结构上的变化。比如: @@ -34,68 +34,68 @@ JavaScript 是多范式语言,意味着它支持多种风格的编程。其他 bar: 'baz' }; - foo.bar = 'qux'; // mutation + foo.bar = 'qux'; // 改变 对象通常需要可变性以便于被方法更新值,在命令式的语言中,大部分的数据结构可变以便于数组和对象的高效操作。 下面是一些函数式语言拥有但是 JavaScript 没有的特性: 1. **纯粹性:**在一些函数式语言中,纯粹性是强制的,有副作用的表达式是不被允许的。 -2. **不可变性:**一些函数式语言不允许转变,采用表达式来产生新的数据结构来代替更改一个已存的数据结构,比如说数组或者对象。这样看起来可能不够搞笑,但是大多数函数式语言在引擎下使用 trie 数据结构,具有结构共享的特点:意味着旧的对象和新的对象是对相同数据的引用。 +2. **不可变性:**一些函数式语言不允许转变,采用表达式来产生新的数据结构来代替更改一个已存的数据结构,比如说数组或者对象。这样看起来可能不够高效,但是大多数函数式语言在引擎下使用 trie 数据结构,具有结构共享的特点:意味着旧的对象和新的对象是对相同数据的引用。 3. **递归:**递归是函数引用自身来进行迭代的能力。在大多数函数式语言中,递归是迭代的唯一方式,它们没有像 `for` 、`while`、`do` 这类循环语句。 **纯粹性:**在 JavaScript 中,纯粹性由约定来达成,如果你不是使用纯函数来构成你的大多数应用,那么你就不是在进行函数式风格的编程。很不幸,在 JavaScript 中,你很容易就会不小心创建和使用一些不纯的函数。 -**不可变性:**在纯函数式语言中,可变性通常是强制的,JavaScript 缺少函数式语言中高效的、基于 trie 树的数据结构,但是又一些你可以使用的库,包括 [Immutable.js](https://facebook.github.io/immutable-js/) 和 [Mori](https://github.com/swannodette/mori),真是期望未来的 ECMAScript 规范版本可以拥抱不可变数据结构。 +**不可变性:**在纯函数式语言中,不可变性通常是强制的,JavaScript 缺少函数式语言中高效的、基于 trie 树的数据结构,但是你可以使用一些库,包括 [Immutable.js](https://facebook.github.io/immutable-js/) 和 [Mori](https://github.com/swannodette/mori),由衷期望未来的 ECMAScript 规范版本可以拥抱不可变数据结构。 -有一些未来的迹象,比如说在 ES6 中添加了 `const` 关键字,`const` 声明的变量不能被重新赋值,知道 `const` 并不实际代表不可改变的值也很重要。 +有一些迹象带来了希望,比如说在 ES6 中添加了 `const` 关键字,`const` 声明的变量不能被重新赋值,重要的是要理解 `const` 所声明的值并不是不可改变的。 -`const` 声明的对象不能被重新声明为新的对象,但是对象的属性却是可变的,JavaScript 有 `freeze()` 对象的能力,但是这些对象只能在根实例上被冻结,意味着嵌套着的对象还是可以改变它的属性。换句话说,在 JavaScript 规范中看到真正的不可变还有很多路要走。 +`const` 声明的对象不能被重新声明为新的对象,但是对象的属性却是可变的,JavaScript 有 `freeze()` 对象的能力,但是这些对象只能在根实例上被冻结,意味着嵌套着的对象还是可以改变它的属性。换句话说,在 JavaScript 规范中看到真正的不可变还有很长的路要走。 -**递归:**JavaScript 支持递归,但是大多数函数式语言都有尾部调用优化的特性,尾部调用优化是一个允许递归的函数重用堆栈帧来递归调用的特性。 +**递归:**JavaScript 技术上支持递归,但是大多数函数式语言都有尾部调用优化的特性,尾部调用优化是一个允许递归的函数重用堆栈帧来递归调用的特性。 -没有尾部调用优化,一个调用的栈很可能没有边界导致堆栈溢出。JavaScript 在 ES6 规范中有一个有限的尾部调用优化。不幸的是,只有一个主要的浏览器引擎支持它,这个优化被部分应用随后从 Babel(最流行的 JavaScript 编译器,在旧的浏览器中被用来把 ES6 编译到 ES5)。 +没有尾部调用优化,一个调用的栈很可能没有边界导致堆栈溢出。JavaScript 在 ES6 规范中有一个有限的尾调用优化。不幸的是,只有一个主要的浏览器引擎支持它,这个优化被部分应用随后从 Babel(最流行的 JavaScript 编译器,在旧的浏览器中被用来把 ES6 编译到 ES5) 中移除。 -最后一行:现在使用递归来作为大的迭代还不是很安全 — 即使你很小心的调用尾部的函数。 +最重要的事实:现在使用递归来作为大的迭代还不是很安全 — 即使你很小心的调用尾部的函数。 -### 什么又是 JavaScript 所有但是纯函数式语言没有的 +### 什么又是 JavaScript 拥有但是纯函数式语言缺乏的 -一个纯粹主义者会告诉你 JavaScript 的可变性是它的重要缺点,这是事实。但是,引起的副作用和改变有时候很有用。事实上,不可能在规避所有副作用的情况下开发有用的现代应用。纯函数式语言比如说 Haskell 使用副作用,但是使用 monads 包来伪装纯函数,从而使程序保持纯净,尽管用 monads 所带来的副作用是不纯净的。 +一个纯粹主义者会告诉你 JavaScript 的可变性是它的重大缺点,这是事实。但是,引起的副作用和改变有时候很有用。事实上,不可能在规避所有副作用的情况下开发有用的现代应用。纯函数式语言比如说 Haskell 使用副作用,使用 monads 包将有副作用的函数伪装成纯函数,从而使程序保持纯净,尽管用 Monads 所带来的副作用是不纯净的。 -monads 的问题是,尽管它的使用很简单,但是对一个不是很熟悉它的人解释清楚它有点像对牛谈琴。 +Monads 的问题是,尽管它的使用很简单,但是对一个不是很熟悉它的人解释清楚它有点像“对牛谈琴”。 -> “monad 是 endofunctor 范畴的幺半群,有什么问题?” ~James Iry 所引用 Philip Wadler的话,解释一个 Saunders Mac Lane 说过的名言。[*“A Brief, Incomplete, and Mostly Wrong History of Programming Languages”*](http://james-iry.blogspot.com/2009/05/brief-incomplete-and-mostly-wrong.html) +> “Monad说白了不过就是自函子范畴上的一个幺半群而已,这有什么难以理解的?” ~James Iry 所引用 Philip Wadler 的话,解释一个 Saunders Mac Lane 说过的名言。[*“编程语言简要、不完整之黑历史”*](http://james-iry.blogspot.com/2009/05/brief-incomplete-and-mostly-wrong.html) -典型的,这是在调侃这有趣的一点。在上面的引用中,关于 monads 的解释相比最初的有了很大的简化,原来是下面这样: +典型的,这是在调侃这有趣的一点。在上面的引用中,关于 Monads 的解释相比最初的有了很大的简化,原来是下面这样: > “`X` 中的 monad 是其 endofunctor 范畴的幺半群,生成 endofunctor 和被 endofunctor 单位 set 组合所代替的 `X` ” ~ Saunders Mac Lane。 [*"Categories for the Working Mathematician"*](https://www.amazon.com/Categories-Working-Mathematician-Graduate-Mathematics/dp/0387984038//ref=as_li_ss_tl?ie=UTF8&linkCode=ll1&tag=eejs-20&linkId=de6f23899da4b5892f562413173be4f0) -尽管这样,在我的观点看来,害怕 monads 是没有必要的,学习 monads 最好的方法不是去读关于它的一堆书和博客,而是立刻去使用它。对于大部分的函数式编程语言来说,晦涩的学术词汇比它实际概念难的多,相信我,你不必通过了解 Saunders Mac Lane 来了解函数式编程。 +尽管这样,在我的观点看来,害怕 Monads 是没有必要的,学习 Monads 最好的方法不是去读关于它的一堆书和博客,而是立刻去使用它。对于大部分的函数式编程语言来说,晦涩的学术词汇比它实际概念难的多,相信我,你不必通过了解 Saunders Mac Lane 来了解函数式编程。 -尽管它不是对所有的编程风格都完全的理想,JavaScript 无疑是作为适应各种编程风格和背景的人的通用编程语言被设计出来的。 +尽管它不是对所有的编程风格都绝对完美,JavaScript 无疑是作为适应各种编程风格和背景的人的通用编程语言被设计出来的。 根据 [Brendan Eric](https://brendaneich.com/2008/04/popularity/) 所言,在一开始的时候,网景公司就有意适应两类开发者: > “...写组件的,比如说 C++ 或者 Java;写脚本的、业余的和爱好者,比如直接写嵌在 HTML 里的代码的。” -最初,网景公司的意向是支持两种不同的语言,同时脚本语言大致要像 Scheme (一个 Lisp 的方言),而且,Brendan Eich: +本来,网景公司的意向是支持两种不同的语言,同时脚本语言大致要像 Scheme (一个 Lisp 的方言),而且,Brendan Eich: > “我被招聘到网景公司,目的是在浏览器中 **做一些 Scheme**”。 JavaScript 应当是一门新的语言: -> “上面工程管理的命令是这门语言**应当像 Java**,这就排除了 Perl,Python,和 Tcl,以及 Scheme。” +> “上级工程管理的命令是这门语言**应当像 Java**,这就排除了 Perl,Python,和 Tcl,以及 Scheme。” 所以,Brendan Eich 最初脑子里的想法是: 1. 浏览器中的 Scheme。 2. 看起来像 Java。 -最终的结果稍微有点复杂: +它最终更像是个大杂烩: ->“我不骄傲,但我很高兴我选择了 Scheme 的一类函数和 Self(尽管奇怪)的原型作为主要的元素。”由于 Java 的影响,特别是 y2k 的 Date 问题以及对象的区别(比如string 和 String),就不幸了。” +>“我不骄傲,但我很高兴我选择了 Scheme 的一类函数和 Self(尽管奇怪)的原型作为主要的元素。”由于 Java 的影响,特别是 y2k 的 Date 问题以及对象的区别(比如 string 和 String),就不幸了。” -我列出了这些 “不好的” 的类 Java 特性,最后转换成 JavaScript: +我列出了这些 “不好的” 的类 Java 特性,最后整理成 JavaScript: * 构造函数和 `new` 关键子,跟工厂函数有着不同的调用和使用语义。 * `class` 的关键字和单一父类 `extends` 作为最初的继承机制。 @@ -107,31 +107,31 @@ JavaScript 应当是一门新的语言: 我们最终创作了一个直接被浏览器支持的语言:JavaScript。 -那意味着浏览器可以减少臃肿和问题,因为它们现在只需要支持一种语言:JavaScript。你也许认为 WebAssembly 是异常,但是 WebAssembly 设计之初的目的是使用兼容的抽象语法树来共享JavaScript的语言绑定(AST)。事实上,最早的把 WebAssembly 编译成 JavaScript 的子集的示范是 ASM.js。 +那意味着浏览器可以减少臃肿和问题,因为它们现在只需要支持一种语言:JavaScript。你也许认为 WebAssembly 是例外,但是 WebAssembly 设计之初的目的是使用兼容的抽象语法树来共享 JavaScript 的语言绑定(AST)。事实上,最早的把 WebAssembly 编译成 JavaScript 的子集的示范是 ASM.js。 作为 web 平台唯一的通用标准编程语言,JavaScript 在软件历史潮流中乘风直上: App 吞食世界, web 吞食 app, 同时 JavaScript 吞食 web。 -根据[各方](http://redmonk.com/sogrady/2016/07/20/language-rankings-6-16/)[调查](http://stackoverflow.com/research/developer-survey-2016),[JavaScript](https://octoverse.github.com/)是目前世界上最流行的语言。 +根据[多个平台](http://redmonk.com/sogrady/2016/07/20/language-rankings-6-16/)[调查](http://stackoverflow.com/research/developer-survey-2016),[JavaScript](https://octoverse.github.com/) 是目前世界上最流行的语言。 -JavaScript 并不是函数式编程的理想化工具,但是它却是为大型的分布式的团队开发大型应用的好工具,因为不同的团队也许有不同建立应用的想法。 +JavaScript 并不是函数式编程的理想化工具,但是它却是为大型的分布式的团队开发大型应用的好工具,因为不同的团队对于如何构建一个应用或许有不同的看法。 一些团队致力于脚本化,那么命令式的编程就特别有用,另外一些更精于抽象架构,那么一点保留的面向对象方法也许不失为坏。还有一些拥抱函数式编程,使用纯函数来确保稳定性、可测试性和项目状态管理以便减少用户的反馈。团队里的这些人可以使用相同的语言,意味着他们可以更好的交换想法,互相学习和在其他人的基础上更进一步的开发。 在 JavaScript 中,所有这些想法可以共存,这样就让更多的人开始拥抱 JavaScript,然后就产生了[世界上最大的开源包管理器](http://www.modulecounts.com/) (2017 年 2 月),[npm](https://www.npmjs.com/)。 -JavaScript 的真正优势在于其生态系统中的思想和用户的多样性。它也许不是纯函数式编程的理想语言,但它是你可以想象的工作在不同平台的人共同合作的理想语言,比如说 Java、Lisp 或者 C。JavaScript 也许对这些有这些背景的用户完全友好,但是这些人很乐意学习这门语言并迅速投入生产。 +JavaScript 的真正优势在于其生态系统中的思想和用户的多样性。它也许不是纯函数式编程最理想的语言,但它是你可以想象的工作在不同平台的人共同合作的理想语言,比如说 Java、Lisp 或者 C。JavaScript 也许并不对有这些背景的用户完全友好,但是这些人很乐意学习这门语言并迅速投入生产。 -我同意 JavaScript 并不是函数式编程着最好的语言。但是,没有任何其他语言可以声称他们可以被所有人使用,同时正如 ES6 所述:JavaScript 可以满足到更与喜欢函数式编程的人的需要,同时也越来越好。相比于抛弃 JavaScript 和它不可思议的被世界上所有公司使用的生态系统,为什么不拥抱它,把它变成一个更适合软件组成化的语言? +我同意 JavaScript 并不是对函数式编程者最好的语言。但是,没有任何其他语言可以声称他们可以被所有人使用,同时正如 ES6 所述:JavaScript 可以满足到更与喜欢函数式编程的人的需要,同时也越来越好。相比于抛弃 JavaScript 和世界上几乎每家公司都使用的令人难以置信的生态系统,为什么不拥抱它,把它变成一个更适合软件组合化的语言? -现在,JavaScript 已经是一门足够优秀的函数式编程语言,意味着人们可以使用 JavaScript 的函数式编程方法来构造所有有趣的和有用的东西。Netflix(和其他使用 Angular 2+ 的应用)使用基于 RxJS 的函数式功能。[Facebook](https://github.com/facebook/react/wiki/sites-using-react)在 React 中使用纯函数、高阶函数和高级组件来开发 Facebook 和 Instagram,[PayPal, KhanAcademy, and Flipkart](https://github.com/reactjs/redux/issues/310)使用 Redux 来进行状态管理。 +现在,JavaScript 已经是一门**足够优秀**的函数式编程语言,意味着人们可以使用 JavaScript 的函数式编程方法来构造很多有趣的和有用的东西。Netflix(和其他使用 Angular 2+ 的应用)使用基于 RxJS 的函数式功能。[Facebook](https://github.com/facebook/react/wiki/sites-using-react)在 React 中使用纯函数、高阶函数和高级组件来开发 Facebook 和 Instagram,[PayPal、KhanAcademy、和Flipkart](https://github.com/reactjs/redux/issues/310)使用 Redux 来进行状态管理。 -它们并不孤单:Angular、React、Redux 和 Lodash 是 JavaScript 生态系统中主要的框架和库,同时它们都被函数式编程很深的影响到 — 或者在 Lodash 和 Redux中,明确的表达是为了在实际的 JavaScript 应用中使用函数式编程模式。 +它们并不孤单:Angular、React、Redux 和 Lodash 是 JavaScript 生态系统中主要的框架和库,同时它们都被函数式编程很深的影响到— 在 Lodash 和 Redux 中,明确地表达是为了在实际的 JavaScript 应用中使用函数式编程模式。 -“为什么是 JavaScript?”因为实际上所有的软件呢公司用来开发软件的语言。无论如何,JavaScript 从 Lisp 这个数十年来的标志头上偷取了 “最受欢迎的函数式编程语言” 的头衔。事实上,Haskell 更适合当今函数式编程概念的标准,但是人们并不使用它来开发实际应用。 +“为什么是 JavaScript?”因为 JavaScript 是实际上大多数公司开发真实的软件所使用的语言。无论你对它是爱是恨,JavaScript 已经取代了 Lisp 这个数十年来 “最受欢迎的函数式编程语言”。事实上,Haskell 更适合当今函数式编程概念的标准,但是人们并不使用它来开发实际应用。 -在任何时候,在美国都有一百万的 JavaScript 工作需求,世界其他地方也有数百万的量。学习 Haskell 可以帮助你很好的学习函数式编程,但学习 JavaScript 将会教会你在实际工作中开发应用。 +在任何时候,在美国都有近十万的 JavaScript 工作需求,世界其他地方也有数十万的量。学习 Haskell 可以帮助你很好的学习函数式编程,但学习 JavaScript 将会教会你在实际工作中开发应用。 App 正在吞食世界, web 正在吞食 app, 同时 JavaScript 正在吞食 web。 From c019bf6346020a777f1149e030de6607bf819294 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E6=99=93=E5=86=9B?= Date: Thu, 13 Apr 2017 00:42:01 +0800 Subject: [PATCH 141/638] functors & category --- TODO/functors-categories.md | 173 ++++++++++++++++++------------------ 1 file changed, 88 insertions(+), 85 deletions(-) diff --git a/TODO/functors-categories.md b/TODO/functors-categories.md index a5b81f432c4..31ee4219b6f 100644 --- a/TODO/functors-categories.md +++ b/TODO/functors-categories.md @@ -1,120 +1,121 @@ > * 原文地址:[Functors & Categories](https://medium.com/javascript-scene/functors-categories-61e031bac53f) > * 原文作者:[Eric Elliott](https://medium.com/@_ericelliott?source=post_header_lockup) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 译者: +> * 译者:[yoyoyohamapi](https://github.com/yoyoyohamapi) [reid3290](https://github.com/reid3290) > * 校对者: -# Functors & Categories # - -## Composable Software ## +# Functor 与 Category (软件编写)(第五部分)# -Smoke Art Cubes to Smoke — MattysFlicks — (CC BY 2.0) -> Note: This is part of the “Composing Software” series on learning functional programming and compositional software techniques in JavaScript ES6+ from the ground up. Stay tuned. There’s a lot more of this to come! -> [< Previous](https://medium.com/javascript-scene/reduce-composing-software-fe22f0c39a1d#.w4y0mlpcs) | [<< Start over at Part 1](https://medium.com/javascript-scene/the-rise-and-fall-and-rise-of-functional-programming-composable-software-c2d91b424c8c#.2dfd6n6qe) +Smoke Art Cubes to Smoke — MattysFlicks — (CC BY 2.0) (译注:该图是用 PS 将烟雾处理成方块状后得到的效果,参见 [flickr](https://www.flickr.com/photos/68397968@N07/11432696204)。)) + +> 注意:这是 “软件编写” 系列文章的第四部分,该系列主要阐述如何在 JavaScript ES6+ 中从零开始学习函数式编程和组合化软件(compositional software)技术(译注:关于软件可组合性的概念,参见维基百科 [Composability](https://en.wikipedia.org/wiki/Composability))。后续还有更多精彩内容,敬请期待! +> [<上一篇](https://medium.com/javascript-scene/reduce-composing-software-fe22f0c39a1d#.w4y0mlpcs) | [<< 返回第一章](https://medium.com/javascript-scene/the-rise-and-fall-and-rise-of-functional-programming-composable-software-c2d91b424c8c#.2dfd6n6qe) -A **functor** is something that can be mapped over. In other words, it’s a container which has an interface which can be used to apply a function to the values inside it. When you see the word functor, you should think *“mappable”.* +所谓 **functor(函子)**,是能够对其进行 map 操作的对象。换言之,**functor** 可以被认为是一个容器,该容器容纳了一个值,并且暴露了一个接口(译注:即 map 接口),该接口使得外界的函数能够操纵容器中的值。所以当你见到 **functor**,别被其来自范畴学的名字唬住,简单把他当做个 *“mappable”* 对象就行。 -The term “functor” comes from category theory. In category theory, a functor is a mapping between categories. Loosely, a **category** is a group of things, where each “thing” can be any value. In code, a functor is sometimes represented as an object with a `.map()` method that maps from one set of values to another. +**“functor”** 一词源于范畴学。在范畴学中,一个 functor 代表了两个范畴(category)间的映射。简单说来,一个 **范畴** 是一系列事物的分组,这里的 “事物” 可以指代一切的值。对于编码来说,一个 functor 通常代表了一个具有 `.map()` 方法的对象,该方法能够将某一集合映射到另一集合。 -A functor supplies a box with zero or more things inside, and a mapping interface. An array is a good example of a functor, but many other kinds of objects can be mapped over as well, including single valued-objects, streams, trees, objects, etc… +上文说到,一个 functor 可以被看做是一个容器,比如我们将其看做是一个盒子,盒子里面容纳了一些事物,或者空空如也,最重要的是,盒子暴露了一个 mapping(映射)接口。在 JavaScript 中,数组对象就是 functor 的绝佳例子(译注:`[1,2,3].map(x => x + 1)`),但是,其他类型的对象,只要能够被 map 操作,也可以算作是 functor,这些对象包括了单值对象(single valued-objects),流(streams),树(trees),对象(objects)等等。 -For collections (arrays, streams, etc…), `.map()` typically iterates over the collection and applies the given function to each value in the collection, but not all functors iterate. +对于如数组和流这样的集合(collections)来说,`.map()` 方法指的是,在集合上进行迭代操作,在此过程中,应用一个预先指定的函数对每次迭代到的值进行处理。但是,不是所有的 functor 都可以被迭代。 -In JavaScript, Arrays and Promises are functors (`.then()` obeys the functor laws), but lots of libraries exist that will turn a variety of other things into functors, too. +在 JavaScript 中,数组和 Promise 对象都是 **functor**(Promise 对象虽然没有 `.map()` 方法,但其 `.then()` 方法也遵从 functor 的定律),除此之外,非常多的第三方库也能够将各种各样的一般事物给转换成 functor(译注:大名鼎鼎的 [Bluebird](https://github.com/petkaantonov/bluebird/) 就能将异步过程封装为 Promise functor)。 -In Haskell, the functor type is defined as: +在 Haskell 中,functor 类型被定义为如下形式: ``` fmap :: (a -> b) -> f a -> f b ``` -Given a function that takes an `a` and returns a `b` and a functor with zero or more `a`s inside it: `fmap` returns a box with zero or more `b`s inside it. The `f a` and `f b` bits can be read as “a functor of `a`” and “a functor of `b`”, meaning `f a` has `a`s inside the box, and `f b` has `b`s inside the box. +fmap 接受一个函数参数,该函数接受一个参数 `a`,并返回一个 `b`,最终,fmap 完成了从 `f a` 到 `f b` 的映射。`f a` 及 `f b` 可以被读作 “一个 `a` 的 functor” 和“一个 `b` 的 functor”,亦即 `f a` 这个盒子容纳了 `a`,`f b` 这个盒子容纳了 `b`。 -Using a functor is easy — just call `map()`: +使用一个 functor 是非常简单的,仅需要调用 `map()` 方法即可: ``` const f = [1, 2, 3]; f.map(double); // [2, 4, 6] ``` -### Functor Laws ### +### Functor 定律 ### -Categories have two important properties: +一个范畴含有两个基本的定律: -1. Identity -2. Composition +1. 同一性(Identity) +2. 组合性(Composition) -Since a functor is a mapping between categories, functors must respect identity and composition. Together, they’re known as the functor laws. +由于 functor 是两个范畴间的映射,其就必须遵守同一性和组合性,二者也构成了 functor 的基本定律。 -### Identity ### +### 同一性 ### -If you pass the identity function (`x => x`) into `f.map()`, where `f` is any functor, the result should be equivalent to (have the same meaning as) `f`: +如果你将函数(`x => x`)传入 `f.map()`,并且,`f` 是任意的一个 functor,那么 `f.map()` 执行结果就等于 `f`。 ``` const f = [1, 2, 3]; f.map(x => x); // [1, 2, 3] ``` -### Composition ### +### 组合性 ### -Functors must obey the composition law: `F.map(x => f(g(x)))` is equivalent to `F.map(g).map(f)`. +functor 还必须具有组合性:`F.map(x => f(g(x))) == F.map(g).map(f)` -Function Composition is the application of one function to the result of another, e.g., given an `x` and the functions, `f` and `g`, the composition `(f ∘ g)(x)` (usually shortened to `f ∘ g` - the `(x)` is implied) means `f(g(x))`. +函数组合是将一个函数的输出作为另一个函数输入的过程。例如,给定一个值 `x`及函数 `f` 和函数 `g`,函数的组合就是 `(f ∘ g)(x)`(通常简写为 `f ∘ g`,简写形式已经暗示了 `(x)`),其意味着 `f(g(x))`。 -A lot of functional programming terms come from category theory, and the essence of category theory is composition. Category theory is scary at first, but easy. Like jumping off a diving board or riding a roller coaster. Here’s the foundation of category theory in a few bullet points: +很多函数式编程的术语都源于范畴学,而范畴学的实质即是组合。初看范畴学,就像初次进行高台跳水或者乘坐过山车,慌张,恐惧,但是并不难完成。你只需明确下面几个范畴学基础要点: -- A category is a collection of objects and arrows between objects (where “object” can mean literally anything). -- Arrows are known as morphisms. Morphisms can be thought of and represented in code as functions. -- For any group of connected objects, `a -> b -> c`, there must be a composition which goes directly from `a -> c`. -- All arrows can be represented as compositions (even if it’s just a composition with the object’s identity arrow). All objects in a category have identity arrows. +- 一个范畴(category)是一个容纳了一系列对象及对象间箭头(`->`)的集合。 +- 箭头只是形式上的描述,实际上,箭头代表了态射(morphismms)。在编程中,态射可以被认为是函数。 +- 对于任何被箭头相连接的对象,如 `a -> b -> c`,必须存在一个 `a -> c ` 的组合。 +- 所有的箭头表示都代表了组合(即便这个对象间的组合只是一个同一(identity)箭头:`a->c`)。所有的对象都存在一个同一箭头,即存在同一态射(`a -> a`)。 -Say you have a function `g` that takes an `a` and returns a `b`, and another function `f` that takes a `b` and returns a `c`; there must also be a function `h` that represents the composition of `f` and `g`. So, the composition from `a -> c`, is the composition `f ∘ g` (`f`*after*`g`). So, `h(x) = f(g(x))`. Function composition works right to left, not left to right, which is why `f ∘ g` is frequently called `f`*after*`g`. +如果你有一个函数 `g`,该函数接受一个参数 `a` 并且返回一个 `b`,另一个函数 `f` 接受一个 `b` 并返回一个 `c`。那么,必然存在一个函数 `h`,其代表了 `f` 及 `g` 的组合。而 `a -> c` 的组合,就是 `f ∘ g`(读作`f` **紧接着** `g`),进而,也就知 `h(x)=f(g(x))`。函数组合的方向是由右向左的,这也就是就是 `f ∘ g` 常被叫做 `f` **紧接着** `g` 的原因。 -Composition is associative. Basically that means that when you’re composing multiple functions (morphisms if you’re feeling fancy), you don’t need parenthesis: +函数组合是满足结合律的,这就意味着你在组合多个函数时,免去了添加括号的烦恼: ``` h∘(g∘f) = (h∘g)∘f = h∘g∘f ``` -Let’s take another look at the composition law in JavaScript: +让我们再看一眼 JavaScript 中组合律: -Given a functor, `F`: +给定一个 functor,`F`: ``` const F = [1, 2, 3]; ``` -The following are equivalent: +下面的两段是等效的: ``` F.map(x => f(g(x))); -// is equivalent to... +// 等效于。。。 F.map(g).map(f); ``` -### Endofunctors ### +> 译注:functor 中函数组合的结合率可以被理解为:对 functor 中保存的值使用组合后的函数进行 map,等效于先后对该值用不同的函数进行 map。 + +### Endofunctors(自函子) ### -An endofunctor is a functor that maps from a category back to the same category. +一个 endofunctor(自函子)是一个能将一个范畴映射回相同范畴的 functor。 -A functor can map from category to category: `F a -> F b` +一个 functor 能够完成任意范畴间映射: `F a -> F b` -An endofunctor maps from a category to the same category: `F a -> F a` +一个 endofunctor 能够完成相同范畴间的映射:`F a -> F a` -`F` here represents a *functor type* and `a` represents a category variable (meaning it can represent any category, including a set or a category of all possible values in a data type). +在这里,`F` 代表了一个 **functor 类型**,而 `a` 代表了一个范畴变量(意味着其能够代表任意的范畴,无论是一个集合,还是一个包含了某一数据类型所有可能取值的范畴)。 -A monad is an endofunctor. Remember: +而一个 monad 则是一个 endofunctor,先记住下面这句话: -> *“A monad is just a monoid in the category of endofunctors. What’s the problem?”* +> “monad 是 endofunctor 范畴的 monoids(幺半群),有什么问题?”(译注:这句话的出处在该系列第一篇已有提及) -Hopefully that quote is starting to make a little more sense. We’ll get to monoids and monads later. +现在,我们希望第一篇提及的这句话能在之后多一点意义,monoids(幺半群)及 monad 将在之后作介绍。 -### Build Your Own Functor ### +### 自定义一个 Functor ### -Here’s a simple example of a functor: +下面将展示一个简单的 functor 例子: ``` const Identity = value => ({ @@ -122,11 +123,11 @@ const Identity = value => ({ }); ``` -As you can see, it satisfies the functor laws: +显然,其满足了 functor 定律: ``` -// trace() is a utility to let you easily inspect -// the contents. +// trace() 是一个简单的工具函数来帮助审查内容 +// 内容 const trace = x => { console.log(x); return x; @@ -134,14 +135,14 @@ const trace = x => { const u = Identity(2); -// Identity law +// 同一性 u.map(trace); // 2 u.map(x => x).map(trace); // 2 const f = n => n + 1; const g = n => n * 2; -// Composition law +// 组合性 const r1 = u.map(x => f(g(x))); const r2 = u.map(g).map(f); @@ -149,11 +150,11 @@ r1.map(trace); // 5 r2.map(trace); // 5 ``` -Now you can map over any data type, just like you can map over an array. Nice! +现在,你可以对存在该 functor 中的任何数据类型进行 map 操作,就像你对一个数组进行 map 时那样。这简直太美妙了。 -That’s about as simple as a functor can get in JavaScript, but it’s missing some features we expect from data types in JavaScript. Let’s add them. Wouldn’t it be cool if the `+` operator could work for number and string values? +上面的代码片展示了 JavaScript 中 functor 的简单实现,但是其缺失了 JavaScript 中常见数据类型的一些特性。现在我们逐个添加它们。首先,我们会想到,假如能够直接通过 `+` 操作符操作我们的 functor 是不是太好了,就像我们在数值或者字符串对象间使用 `+` 号那样。 -To make that work, all we need to do is implement `.valueOf()` -- which also seems like a convenient way to unwrap the value from the functor: +为了使该想法变现,我们首先要为该 functor 对象添加 `.valueOf()` 方法 —— 这可被看作是提供了一个便捷的渠道来将值从 functor 盒子中取出。 ``` const Identity = value => ({ @@ -169,13 +170,13 @@ const hi = (Identity('h') + Identity('i')); trace(hi); // "hi" ``` -Nice. But what if we want to inspect an `Identity` instance in the console? It would be cool if it would say `"Identity(value)"`, right. Let's add a `.toString()` method: +现在代码更漂亮了。但是如果我们还想要在控制台审查 `Identity` 实例呢?如果控制台能够输出 `"Identity(value)"` 就太好了,为此,我们只需要添加一个 `.toString()` 方法即可(译注:亦即重载原型链上原有的 `.toString()` 方法): ``` toString: () => `Identity(${value})`, ``` -Cool. We should probably also enable the standard JS iteration protocol. We can do that by adding a custom iterator: +代码又有所进步。现在,我们可能也想 functor 能够满足标准的 JavaScript 迭代协议(译注:[MDN - 迭代协议](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Iteration_protocols))。为此,我们可以为 `Identity` 添加一个自定义的迭代器: ``` [Symbol.iterator]: () => { @@ -197,7 +198,7 @@ Cool. We should probably also enable the standard JS iteration protocol. We can }, ``` -Now this will work: +现在,我们的 functor 还能这样工作: ``` // [Symbol.iterator] enables standard JS iterations: @@ -205,28 +206,30 @@ const arr = [6, 7, ...Identity(8)]; trace(arr); // [6, 7, 8] ``` -What if you want to take an `Identity(n)` and return an array of Identities containing `n + 1`, `n + 2`, and so on? Easy, right? +假如你想借助 `Identity(n)` 来返回包含了 `n+1`,`n+2` 等等的 Identity 数组,这非常容易: ``` const fRange = ( start, end ) => Array.from( - { length: end - start + 1 }, + {length: end - start + 1}, (x, i) => Identity(i + start) ); ``` -Ah, but what if you want this to work with any functor? What if we had a spec that said that each instance of a data type must have a reference to its constructor? Then you could do this: +> 译注:[MDN -- Array.from()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/from) + +但是,如果你想上面的操作方式能够应用于任何 functor,该怎么办?假如我们规定了每种数据类型对应的实例必须有一个关于其构造函数的引用,那么你可以这样改造之前的逻辑: ``` const fRange = ( start, end ) => Array.from( - { length: end - start + 1 }, + {length: end - start + 1}, - // change `Identity` to `start.constructor` + // 将 `Identity` 变更为 `start.constructor` (x, i) => start.constructor(i + start) ); @@ -234,7 +237,7 @@ const range = fRange(Identity(2), 4); range.map(x => x.map(trace)); // 2, 3, 4 ``` -What if you want to test to see if a value is a functor? We could add a static method on `Identity` to check. We should throw in a static `.toString()` while we're at it: +假如你还想知道一个值是否在一个 functor 中,又怎么办?我们可以为 `Identity` 添加一个静态方法 `.is()` 来进行检测,另外,我们也顺便添加了一个静态的 `.toString()` 方法来告知这个 functor 的种类: ``` Object.assign(Identity, { @@ -243,7 +246,8 @@ Object.assign(Identity, { }); ``` -Let’s put all this together: + +现在,我们整合一下上面的代码片: ``` const Identity = value => ({ @@ -280,14 +284,14 @@ Object.assign(Identity, { }); ``` -Note you don’t need all this extra stuff for something to qualify as a functor or an endofunctor. It’s strictly for convenience. All you *need* for a functor is a `.map()` interface that satisfies the functor laws. +注意,无论是 functor,还是 endofunctor,不一定需要上述那么多的条条框框。以上工作只是为了我们在使用 functor 时更加便捷,而非必须。一个 functor 的所有需求只是一个满足了 functor 定律 `.map()` 接口。 -### Why Functors? ### +### 为什么要使用 functor? ### -Functors are great for lots of reasons. Most importantly, they’re an abstraction that you can use to implement lots of useful things in a way that works with any data type. For instance, what if you want to kick off a chain of operations, but only if the value inside the functor is not `undefined` or `null`? +说 functor 多么多么好是不无理由的。最重要的一点是,functor 作为一种抽象,能让开发者以同一种方式实现大量有用的,能够操纵任何数据类型的事物。例如,如果你想要在 functor 中值不为 `null` 或者不为 `undefined` 前提下,构建一串地链式操作: ``` -// Create the predicate +// 创建一个 predicte const exists = x => (x.valueOf() !== undefined && x.valueOf() !== null); const ifExists = x => ({ @@ -297,9 +301,9 @@ const ifExists = x => ({ const add1 = n => n + 1; const double = n => n * 2; -// Nothing happens... +// undefined ifExists(Identity(undefined)).map(trace); -// Still nothing... +// null ifExists(Identity(null)).map(trace); // 42 @@ -310,9 +314,9 @@ ifExists(Identity(20)) ; ``` -Of course, functional programming is all about composing tiny functions to create higher level abstractions. What if you want a generic map that works with any functor? That way you can partially apply arguments to create new functions. +函数式编程一直探讨的是将各个小的函数进行组合,以创建出更高层次的抽象。假如你想要一个更通用的,能够工作在任何 functor 上的 `map()` 方法,那么你可以通过参数的部分应用(译注:即 [偏函数](https://en.wikipedia.org/wiki/Partial_application))来完成。 -Easy. Pick your favorite auto-curry, or use this magic spell from before: +你可以使用自己喜欢的 curry 化方法(译注:Underscore,Lodash,Ramda 等第三方库都提供了 curry 化一个函数的方法),或者使用下面这个之前篇章提到的,基于 ES6 的,充满魅力的 curry 化方法来实现参数的部分应用: ``` const curry = ( @@ -324,7 +328,7 @@ const curry = ( )([...arr, ...args]); ``` -Now we can customize map: +现在,我们可以自定义 `map()` 方法: ``` const map = curry((fn, F) => F.map(fn)); @@ -335,28 +339,27 @@ const mdouble = map(double); mdouble(Identity(4)).map(trace); // 8 ``` -### Conclusion ### +### 总结 ### -Functors are things we can map over. More specifically, a functor is a mapping from category to category. A functor can even map from a category back to the same category (i.e., an *endofunctor*). +functor 是能够对其进行 map 操作的对象。更进一步地,一个 functor 能够将一个范畴映射到另一个范畴。一个 functor 甚至可以将某一范畴映射回相同范畴(例如 endofunctor)。 -A category is a collection of objects, with arrows between objects. Arrows represent morphisms (aka functions, aka compositions). Each object in a category has an identity morphism (`x => x`). For any chain of objects `A -> B -> C` there must exist a composition `A -> C`. +一个范畴是一个容纳了对象和对象间箭头的集合。箭头代表了态射(也可理解为函数或者组合)。一个范畴中的每个对象都具有一个同一态射(`x -> x`)。对于任何链接起来的对象 `A -> B -> C`,必存在一个 `A -> C` 的组合。 -Functors are great higher-order abstractions that allow you to create a variety of generic functions that will work for any data type. +总之,functor 是一个极佳的高阶抽象,能然你创建各种各样的通用函数来操作任何的数据类型。 -**To be continued…** +**未完待续。。。** -### Next Steps ### +### 接下来 ### -Want to learn more about functional programming in JavaScript? +想学习更多 JavaScript 函数式编程吗? -[Learn JavaScript with Eric Elliott](http://ericelliottjs.com/product/lifetime-access-pass/). If you’re not a member, you’re missing out! +[跟着 Eric Elliott 学 Javacript](http://ericelliottjs.com/product/lifetime-access-pass/),机不可失时不再来! -[ -](https://ericelliottjs.com/product/lifetime-access-pass/) +[](https://ericelliottjs.com/product/lifetime-access-pass/) -***Eric Elliott*** is the author of [*“Programming JavaScript Applications”*](http://pjabook.com) (O’Reilly), and [*“Learn JavaScript with Eric Elliott”*](http://ericelliottjs.com/product/lifetime-access-pass/) . He has contributed to software experiences for **Adobe Systems, Zumba Fitness, The Wall Street Journal, ESPN, BBC**, and top recording artists including **Usher, Frank Ocean, Metallica**, and many more. +**Eric Elliott** 是 [**“编写 JavaScript 应用”**](http://pjabook.com) (O’Reilly) 以及 [**“跟着 Eric Elliott 学 Javascript”**](http://ericelliottjs.com/product/lifetime-access-pass/) 两书的作者。他为许多公司和组织作过贡献,例如 **Adobe Systems**、**Zumba Fitness**、**The Wall Street Journal**、**ESPN** 和 **BBC** 等 , 也是很多机构的顶级艺术家,包括但不限于 **Usher**、**Frank Ocean** 以及 **Metallica**。 -*He spends most of his time in the San Francisco Bay Area with the most beautiful woman in the world.* +大多数时间,他都在 San Francisco Bay Area,同这世上最美丽的女子在一起。 --- From 2c3a7a1b733624be4e533cd2b2fda859caa04ddd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E6=99=93=E5=86=9B?= Date: Thu, 13 Apr 2017 00:50:54 +0800 Subject: [PATCH 142/638] title --- TODO/functors-categories.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO/functors-categories.md b/TODO/functors-categories.md index 31ee4219b6f..491a023bc29 100644 --- a/TODO/functors-categories.md +++ b/TODO/functors-categories.md @@ -4,7 +4,7 @@ > * 译者:[yoyoyohamapi](https://github.com/yoyoyohamapi) [reid3290](https://github.com/reid3290) > * 校对者: -# Functor 与 Category (软件编写)(第五部分)# +# Functor 与 Category (软件编写)(第六部分)# From 8e7e318c4a1391a3088eeb6fbab18e5b61807797 Mon Sep 17 00:00:00 2001 From: sunxinlei Date: Thu, 13 Apr 2017 02:07:57 +0800 Subject: [PATCH 143/638] =?UTF-8?q?=E7=BF=BB=E8=AF=91=E4=B8=80=E8=BF=87?= =?UTF-8?q?=E5=8D=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...uction-to-javascript-composing-software.md | 124 +++++++++--------- 1 file changed, 63 insertions(+), 61 deletions(-) diff --git a/TODO/a-functional-programmers-introduction-to-javascript-composing-software.md b/TODO/a-functional-programmers-introduction-to-javascript-composing-software.md index c831ac252e4..df1ca8810a3 100644 --- a/TODO/a-functional-programmers-introduction-to-javascript-composing-software.md +++ b/TODO/a-functional-programmers-introduction-to-javascript-composing-software.md @@ -1,29 +1,30 @@ > * 原文地址:[A Functional Programmer’s Introduction to JavaScript (Composing Software)(part 3)](https://medium.com/javascript-scene/a-functional-programmers-introduction-to-javascript-composing-software-d670d14ede30) > * 原文作者:[Eric Elliott](https://medium.com/@_ericelliott?source=post_header_lockup) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 译者: +> * 译者:[sun](http://suncafe.cc) > * 校对者: -# A Functional Programmer’s Introduction to JavaScript (Composing Software) # +# 函数式程序员的 JavaScript 简介 (软件构建系列) # -Smoke Art Cubes to Smoke — MattysFlicks — (CC BY 2.0) +烟雾艺术模仿 — MattysFlicks — (CC BY 2.0) > Note: This is part of the “Composing Software” series on learning functional programming and compositional software techniques in JavaScript ES6+ from the ground up. Stay tuned. There’s a lot more of this to come! -> [< Previous](https://medium.com/javascript-scene/why-learn-functional-programming-in-javascript-composing-software-ea13afc7a257#.ia0aq3fmb) | [<< Start over at Part 1](https://medium.com/javascript-scene/the-rise-and-fall-and-rise-of-functional-programming-composable-software-c2d91b424c8c#.2dfd6n6qe) | [Next >](https://medium.com/javascript-scene/higher-order-functions-composing-software-5365cf2cbe99#.egoxjg6x7) +> [< Previous](https://medium.com/javascript-scene/why-learn-functional-programming-in-javascript-composing-software-ea13afc7a257#.ia0aq3fmb) | [<< Start over at Part 1](https://medium.com/javascript-scene/the-rise-and-fall-and-rise-of-functional-programming-composable-software-c2d91b424c8c#.2dfd6n6qe) | [Next >](https://medium.com/javascript-scene/higher-order-functions-composing-software-5365cf2cbe99#.egoxjg6x7) -For those unfamiliar with JavaScript or ES6+, this is intended as a brief introduction. Whether you’re a beginner or experienced JavaScript developer, you may learn something new. The following is only meant to scratch the surface and get you excited. If you want to know more, you’ll just have to explore deeper. There’s a lot more ahead. +对于不熟悉 JavaScript 或 ES6+ 的同学,这里做一个简短的介绍。无论你是 JavaScript 开发新手还是有经验的老兵,你都可能学到一些新东西。以下内容仅是毛羽鳞鬣,吊吊大家的兴致。如果想知道更多,还需深入学习。敬请期待吧。 -The best way to learn to code is to code. I recommend that you follow along using an interactive JavaScript programming environment such as [CodePen](https://codepen.io/) or the [Babel REPL](https://babeljs.io/repl/) . -Alternatively, you can get away with using the Node or browser console REPLs. +学习编程最好的方法就是动手编程。我建议您使用交互式 JavaScript 编程环境(如 [CodePen](https://codepen.io/) 或 [Babel REPL](https://babeljs.io/repl/))。 -### Expressions and Values ### +或者,您也可以使用 Node 或浏览器控制台 REPL。 -An expression is a chunk of code that evaluates to a value. +### 表达式和值 ### -The following are all valid expressions in JavaScript: +表达式是可以求得数据值的代码块。 + +下面这些都是 JavaScript 中合法的表达式: ``` 7; @@ -35,42 +36,42 @@ The following are all valid expressions in JavaScript: 'Hello'; // Hello ``` -The value of an expression can be given a name. When you do so, the expression is evaluated first, and the resulting value is assigned to the name. For this, we’ll use the `const` keyword. It's not the only way, but it's the one you'll use most, so we'll stick with `const` for now: +表达式的值可以被赋予一个名称。执行此操作时,表达式首先被计算,取得的结果值被分配给该名称。对于这一点我们将使用 `const` 关键字。这不是唯一的方式,但这将是你使用最多的,所以目前我们就可以坚持使用 `const`。 ``` const hello = 'Hello'; hello; // Hello ``` -### var, let, and const ### +### var、let 和 const ### -JavaScript supports two more variable declaration keywords: `var`, and `let`. I like to think of them in terms of order of selection. By default, I select the strictest declaration: `const`. A variable declared with the `const` keyword can't be reassigned. The final value must be assigned at declaration time. This may sound rigid, but the restriction is a good thing. It's a signal that tells you, "the value assigned to this name is not going to change". It helps you fully understand what the name means right away, without needing to read the whole function or block scope. +JavaScript 支持另外两种变量声明关键字:`var`,还有 `let`。我喜欢根据选择的顺序来考虑它们。默认情况下,我选择最严格的声明方式:`const`。用 `const` 关键字声明的变量不能被重新赋值。最终值必须在声明时分配。这可能听起来很严格,但限制是一件好事。这是个标识在提醒你“赋给这个名称的值将不会改变”。它可以帮你全面了解这个名称的意义,而无需阅读整个函数或块级作用域。 -Sometimes it’s useful to reassign variables. For example, if you’re using manual, imperative iteration rather than a more functional approach, you can iterate a counter assigned with `let`. +有时,给变量重新赋值很有用。比如,如果你正在写一个手动的强制性迭代,而不是一个更具功能性的方法,你可以迭代一个用 `let` 赋值的计数器。 -Because `var` tells you the least about the variable, it is the weakest signal. Since I started using ES6, I have never intentionally declared a `var` in a real software project. +因为 `var` 能告诉你很少关于这个变量的信息,所以它是最无力的声明标识。自从开始用 ES6,我就再也没在实际软件项目中有意使用 `var` 作声明了。 -Be aware that once a variable is declared with `let` or `const`, any attempt to declare it again will result in an error. If you prefer more experimental flexibility in the REPL (Read, Eval, Print Loop) environment, you may use `var` instead of `const` to declare variables. Redeclaring `var` is allowed. +注意一下,一个变量一旦用 `let` 或 `const` 声明,任何再次声明的尝试都将导致报错。如果你在 REPL(读取-求值-输出循环)环境中更喜欢多一些实验性和灵活性,那么建议你使用 `var` 声明变量,而不是 `const`。 -This text will use `const` in order to get you in the habit of defaulting to `const` for actual programs, but feel free to substitute `var` for the purpose of interactive experimentation. +本文将使用 const 来让您习惯于为实际程序中用 `const`,而出于试验的目的自由切换回 `var`。 -### Types ### +### 数据类型 ### -So far we’ve seen two types: numbers and strings. JavaScript also has booleans (`true` or `false`), arrays, objects, and more. We'll get to other types later. +目前为止我们见到了两种数据类型:数字和字符串。JavaScript 也有布尔值(`true` 或 `false`)、数组、对象等。稍后我们再看其他类型。 -An array is an ordered list of values. Think of it as a box that can hold many items. Here’s the array literal notation: +数组是一系列值的有序列表。可以把它比作一个能够装很多元素的盒子。这是一个数组字面量: ``` [1, 2, 3]; ``` -Of course, that’s an expression which can be given a name: +当然,它也是一个可被赋予名称的表达式: ``` const arr = [1, 2, 3]; ``` -An object in JavaScript is a collection of key: value pairs. It also has a literal notation: +在 JavaScript 中,对象是一系列键值对的集合。它也有字面量: ``` { @@ -78,7 +79,7 @@ An object in JavaScript is a collection of key: value pairs. It also has a liter } ``` -And of course, you can assign an object to a name: +当然,你也可以给对象赋予名称: ``` const foo = { @@ -86,40 +87,40 @@ const foo = { } ``` -If you want to assign existing variables to object property keys of the same name, there’s a shortcut for that. You can just type the variable name instead of providing both a key and a value: +如果你想将现有变量赋值给同名的对象属性,这有个捷径。你可以仅输入变量名,而不用同时提供一个键和一个值: ``` const a = 'a'; -const oldA = { a: a }; // long, redundant way -const oA = { a }; // short an sweet! +const oldA = { a: a }; // 长而冗余的写法 +const oA = { a }; // 短小精悍! ``` -Just for fun, let’s do that again: +为了好玩而已,让我们再来一次: ``` const b = 'b'; const oB = { b }; ``` -Objects can be easily composed together into new objects: +对象可以轻松合并到新的对象中: ``` const c = {...oA, ...oB}; // { a: 'a', b: 'b' } ``` -Those dots are the object spread operator. It iterates over the properties in `oA` and assigns them to the new object, then does the same for `oB`, overriding any keys that already exist on the new object. As of this writing, object spread is a new, experimental feature that may not be available in all the popular browsers yet, but if it's not working for you, there is a substitute: `Object.assign()`: +这些点是对象扩展运算符。它迭代 `oA` 的属性并分配到新的对象中,`oB` 也是一样,在新对象中已经存在的键都会被重写。在撰写本文时,对象扩展是一个新的试验特性,可能还没有被所有主流浏览器支持,但如果你那不能用,还可以用 `Object.assign()` 替代: ``` const d = Object.assign({}, oA, oB); // { a: 'a', b: 'b' } ``` -Only a little more typing in the `Object.assign()` example, and if you're composing lots of objects, it may even save you some typing. Note that when you use `Object.assign()`, you must pass a destination object as the first parameter. It is the object that properties will be copied to. If you forget, and omit the destination object, the object you pass in the first argument will be mutated. +这个 `Object.assign()` 的例子代码很少,如果你想合并很多对象,它甚至可以节省一些打字。注意当你使用 `Object.assign()` 时,你必须传一个目标对象作为第一个参数。它就是那个源对象的属性将被复制过去的对象。如果你忘了传,第一个参数传递的对象将被改变。 -In my experience, mutating an existing object rather than creating a new object is usually a bug. At the very least, it is error-prone. Be careful with `Object.assign()`. +以我的经验,改变一个已经存在的对象而不创建一个新的对象常常引发 bug。至少至少,它很容易出错。要小心使用 `Object.assign()`。 -### Destructuring ### +### 解构 ### -Both objects and arrays support destructuring, meaning that you can extract values from them and assign them to named variables: +对象和数组都支持解构,这意味着你可以从中提取值分配给命过名的变量: ``` const [t, u] = ['a', 'b']; @@ -130,19 +131,19 @@ const blep = { blop: 'blop' }; -// The following is equivalent to: +// 下面等同于: // const blop = blep.blop; const { blop } = blep; blop; // 'blop' ``` -As with the array example above, you can destructure to multiple assignments at once. Here’s a line you’ll see in lots of Redux projects: +和上面数组的例子类似,你可以一次解构多次分配。下面这行你在大量的 Redux 项目中都能见到。 ``` const { type, payload } = action; ``` -Here’s how it’s used in the context of a reducer (much more on that topic coming later): +下面是它在一个 reducer(后面的话题再详细说) 的上下文中的使用方法。 ``` const myReducer = (state = {}, action = {}) => { @@ -154,75 +155,76 @@ const myReducer = (state = {}, action = {}) => { }; ``` -If you don’t want to use a different name for the new binding, you can assign a new name: +如果不想为新绑定使用不同的名称,你可以分配一个新名称: ``` const { blop: bloop } = blep; bloop; // 'blop' ``` -Read: Assign `blep.blop` as `bloop`. +读作:把 `blep.blop` 分配给 `bloop`。 -### Comparisons and Ternaries ### +### 比较运算符和三元表达式 ### -You can compare values with the strict equality operator (sometimes called “triple equals”): +你可以用严格的相等操作符(有时称为“三等于”)来比较数据值: ``` 3 + 1 === 4; // true ``` -There’s also a sloppy equality operator. It’s formally known as the “Equal” operator. Informally, “double equals”. Double equals has a valid use-case or two, but it’s almost always better to default to the `===` operator, instead. +还有另外一种宽松的相等操作符。它正式地被称为“等于”运算符。非正式地可以叫“双等于”。双等于有一两个有效的用例,但大多数时候默认使用 `===` 操作符是更好的选择。 + -Other comparison operators include: +其它比较操作符有: -- `>` Greater than -- `<` Less than -- `>=` Greater than or equal to -- `<=` Less than or equal to -- `!=` Not equal -- `!==` Not strict equal -- `&&` Logical and -- `||` Logical or +- `>` 大于 +- `<` 小于 +- `>=` 大于或等于 +- `<=` 小于或等于 +- `!=` 不等于 +- `!==` 严格不等于 +- `&&` 逻辑与 +- `||` 逻辑或 -A ternary expression is an expression that lets you ask a question using a comparator, and evaluates to a different answer depending on whether or not the expression is truthy: +三元表达式是一个可以让你使用一个比较器来问问题的表达式,运算出的不同答案取决于表达式是否为真: ``` 14 - 7 === 7 ? 'Yep!' : 'Nope.'; // Yep! ``` -### Functions ### +### 函数 ### -JavaScript has function expressions, which can be assigned to names: +JavaScript 支持函数表达式,函数可以这样分配名称: ``` const double = x => x * 2; ``` -This means the same thing as the mathematical function `f(x) = 2x`. Spoken out loud, that function reads `f` of `x` equals `2x`. This function is only interesting when you apply it to a specific value of `x`. To use the function in other equations, you'd write `f(2)`, which has the same meaning as `4`. +这和数学表达式 `f(x) = 2x` 是一个意思。大声说出来,这个函数读作 `x` 的 `f` 等于 `2x`。这个函数只有当你用一个具体的 `x` 的值应用它的时候才有意思。在其它方程式里面你写 `f(2)`,就等同于 `4`。 -In other words, `f(2) = 4`. You can think of a math function as a mapping from inputs to outputs. `f(x)` in this case is a mapping of input values for `x` to corresponding output values equal to the product of the input value and `2`. +换种说话就是 `f(2) = 4`。您可以将数学函数视为从输入到输出的映射。这个例子里 `f(x)` 是输入数值 `x` 到相应的输出数值的映射,等于输入数值和 `2` 的乘积。 -In JavaScript, the value of a function expression is the function itself: +在 JavaScript 中,函数表达式的值是函数本身: ``` double; // [Function: double] ``` -You can see the function definition using the `.toString()` method: +你可以使用 `.toString()` 方法看到这个函数的定义。 ``` double.toString(); // 'x => x * 2' ``` -If you want to apply a function to some arguments, you must invoke it with a function call. A function call applies a function to its arguments and evaluates to a return value. +如果要将函数应用于某些参数,则必须使用函数调用来调用它。函数调用会接收参数并且计算一个返回值。 -You can invoke a function using `(argument1, argument2, ...rest)`. For example, to invoke our double function, just add the parentheses and pass in a value to double: +你可以使用 `(argument1, argument2, ...rest)` 调用一个函数。比如调用我们的 double 函数,就加一对括号并传进去一个值: ``` double(2); // 4 ``` -Unlike some functional languages, those parentheses are meaningful. Without them, the function won’t be called: +和一些函数式语言不同,这对括号是有意义的。没有它们,函数将不会被调用。 ``` double 4; // SyntaxError: Unexpected number @@ -481,7 +483,7 @@ Want to learn more about functional programming in JavaScript? [Learn JavaScript with Eric Elliott](http://ericelliottjs.com/product/lifetime-access-pass/). If you’re not a member, you’re missing out! [ -](https://ericelliottjs.com/product/lifetime-access-pass/) +](https://ericelliottjs.com/product/lifetime-access-pass/) ***Eric Elliott*** is the author of [*“Programming JavaScript Applications”*](http://pjabook.com) (O’Reilly), and [*“Learn JavaScript with Eric Elliott”*](http://ericelliottjs.com/product/lifetime-access-pass/) . He has contributed to software experiences for **Adobe Systems, Zumba Fitness, The Wall Street Journal, ESPN, BBC and top recording artists including Usher, Frank Ocean, Metallica**, and many more. From 1656973000dc535edb97f9447f47325a89444f7d Mon Sep 17 00:00:00 2001 From: Wenlin Ou Date: Wed, 12 Apr 2017 19:19:12 -0400 Subject: [PATCH 144/638] errors fixes --- TODO/nothing-will-change-until-you-start-building.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/TODO/nothing-will-change-until-you-start-building.md b/TODO/nothing-will-change-until-you-start-building.md index 034e8ae1484..f063274822b 100644 --- a/TODO/nothing-will-change-until-you-start-building.md +++ b/TODO/nothing-will-change-until-you-start-building.md @@ -10,15 +10,15 @@ # 真正行动之前 你将一无所成 -就在上周我打了一辆 Lyft 出租车,司机跟我聊了很多关于她的天花乱坠的想法。她的想法里,她有写一本儿童书籍的想法,有开发一个帮助人们找到停车位置的软件的想法,有寻找到一种更高效打包礼物的想法,但问题是她总是犹豫不决:她有很多想法但却不知道从何开始。 +就在上周我打了一辆 Lyft 出租车,司机跟我聊了很多关于她的天花乱坠的想法。她既想写一本儿童书籍,又想开发一个帮助人们找到停车位置的软件,还想找到一种更高效的打包礼物的方式,但问题是她总是犹豫不决:她有很多想法但却不知道从何开始。 -接下来这段话是我对她说的:**开始一些实际行动吧。选一个项目然后尽可能的去完成它。** 如果你想写一本书,那么从每天写一页开始;如果你想做一个APP,那就从构建草图开始。任何人都可以做到这些。 +接下来这段话是我对她说的:**开始一些实际行动吧。选一个项目然后尽可能的去完成它。** 如果你想写一本书,那么从每天写一页开始;如果你想做一个 APP ,那就从构建草图开始。任何人都可以做到这些。 这个建议适用于所有的创作者。一旦你开始着手在你的项目上,你将会不由自主的继续做下去。创新将会成为你人格的一部分 —— 即使你的项目失败了。 你今天迈出的一小步将会是你人生的一大步。看看那些伟大的产品缔造者吧,比如 [Drew Wilson](https://twitter.com/drewwilson), [Pieter Levels](https://twitter.com/levelsio?), 和 [Sebastian Dobrincu](https://twitter.com/Sebyddd)。**他们不仅仅是等待合适的时机。** 他们每周都在更新他们的产品。 -在一个 [IndieHackers](https://www.indiehackers.com/podcast/006-josh-pigford-of-baremetrics) 的节目中, [Josh Pigford](https://twitter.com/Shpigford) 透露,在做 Baremetrics 之前,他已经尝试过 *一百个* 失败的点子。 然而现在, Baremetrics 每个月都会有 60,000 美金进账。 +在一个 [IndieHackers](https://www.indiehackers.com/podcast/006-josh-pigford-of-baremetrics) 的采访中, [Josh Pigford](https://twitter.com/Shpigford) 透露,在做 Baremetrics 之前,他已经尝试过 *一百个* 失败的点子。 然而现在, Baremetrics 每个月都会有 60,000 美金进账。 ![](https://cdn-images-1.medium.com/max/800/1*BzmVaqAKEzNRybUmMYOxdA.png) @@ -39,7 +39,7 @@ ![](https://cdn-images-1.medium.com/max/800/1*ESildSVTSSOnFXGDxD-l9w.png) -另一个让你保持动力的建议就是让你的项目更加公开。在网上和你的同行们分享你的进展。人们很喜欢看到线框(wireframe 翻译成 ”框架“ 会不会好一点?)类的东西,并常常带来有价值的投入。 +另一个让你保持动力的建议就是让你的项目更加公开。在网上和你的同行们分享你的进展。人们很喜欢看到流程图一类的东西,并常常带来有价值的投入。 我自己找到的一个非常有用的保持动力的小秘诀是和朋友打赌。我对他们说,如果我在截止日期前没有完成某个任务,我就给他们一百刀。比如,我会在一个项目开始的两周内找到二十个人打赌。 @@ -83,7 +83,6 @@ 我在 Medium 上每周都会发文。也可以关注我的 [Twitter](https://twitter.com/JonathanZWhite),我会发一些关于设计、前端开发和虚拟现实的杂想。 -*P.S. If you enjoyed this article, it would mean a lot if you click the 💚 and share with friends.*(可删?) --- From 9adaad4fe374f2caf5ea1914f62f9a81d30c60be Mon Sep 17 00:00:00 2001 From: sqrtthree Date: Thu, 13 Apr 2017 10:05:57 +0800 Subject: [PATCH 145/638] :memo: Adjust the translator's information format (#1435) --- TODO/beyond-browser-web-desktop-apps.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TODO/beyond-browser-web-desktop-apps.md b/TODO/beyond-browser-web-desktop-apps.md index f40c59cd68e..56eefc24934 100644 --- a/TODO/beyond-browser-web-desktop-apps.md +++ b/TODO/beyond-browser-web-desktop-apps.md @@ -1,8 +1,8 @@ > * 原文地址:[Beyond The Browser: From Web Apps To Desktop Apps](https://www.smashingmagazine.com/2017/03/beyond-browser-web-desktop-apps/) > * 原文作者:本文已获原作者 [Adam Lynch](https://www.smashingmagazine.com/author/adamlynch/) 授权 > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 译者: [bambooom](https://github.com/bambooom)/[imink](https://github.com/imink) -> * 校对者:[bambooom](https://github.com/bambooom)/[imink](https://github.com/imink)/[sunui](https://github.com/sunui) +> * 译者: [bambooom](https://github.com/bambooom)、[imink](https://github.com/imink) +> * 校对者:[bambooom](https://github.com/bambooom)、[imink](https://github.com/imink)、[sunui](https://github.com/sunui) ## 超越浏览器:从 web 应用到桌面应用 From 38ccbc8b663fc62fb2dce80af2bc6d760b3b4dbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=B9=E5=8F=B7=E4=B8=89?= Date: Thu, 13 Apr 2017 10:32:35 +0800 Subject: [PATCH 146/638] Add image. --- ...-and-rise-of-functional-programming-composable-software.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TODO/the-rise-and-fall-and-rise-of-functional-programming-composable-software.md b/TODO/the-rise-and-fall-and-rise-of-functional-programming-composable-software.md index 608366c3f41..dce44040646 100644 --- a/TODO/the-rise-and-fall-and-rise-of-functional-programming-composable-software.md +++ b/TODO/the-rise-and-fall-and-rise-of-functional-programming-composable-software.md @@ -90,8 +90,8 @@ lambda 表达式对软件设计产生了很大的影响,在 1980 年之前, [![](https://i.embed.ly/1/display/resize?url=https%3A%2F%2Fpbs.twimg.com%2Fprofile_images%2F3722564505%2Fb8030b8f3990875e8f38ed877fdf8d25_bigger.png&key=4fce0568f2ce49e8b54624ef71a8a5bd&width=40)](https://video.twimg.com/tweet_video/CmectVVVUAAsvpo.mp4) - -我一直在告诉大家 [JavaScript] 并不是一门玩具语言。现在我需要展示它。[#StarWars](https://twitter.com/hashtag/StarWars?src=hash) [@BrendanEich](https://twitter.com/BrendanEich) +![](http://ww1.sinaimg.cn/large/006tNbRwgy1fekui0p6i3j30j50hcmyn.jpg) +我一直在告诉大家 #JavaScript 并不是一门玩具语言。现在我需要展示它。 在 2015 年,使用函数的组合来编写软件又开始流行起来。为了更简单化,JavaScript 规范获得的数十年来第一次主要的更新并且添加了箭头函数,为了更简单的编写和读取函数、柯里化,和 lambda 语句。 From d7bb3d2a8c355b62b64ac5c987746005bde080fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=B9=E5=8F=B7=E4=B8=89?= Date: Thu, 13 Apr 2017 10:34:39 +0800 Subject: [PATCH 147/638] Delete unnecessary image. --- ...ll-and-rise-of-functional-programming-composable-software.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/TODO/the-rise-and-fall-and-rise-of-functional-programming-composable-software.md b/TODO/the-rise-and-fall-and-rise-of-functional-programming-composable-software.md index dce44040646..577d3d97d34 100644 --- a/TODO/the-rise-and-fall-and-rise-of-functional-programming-composable-software.md +++ b/TODO/the-rise-and-fall-and-rise-of-functional-programming-composable-software.md @@ -88,8 +88,6 @@ lambda 表达式对软件设计产生了很大的影响,在 1980 年之前, 在 2010 年左右,一些有趣的事情发生了:JavaScript 的崛起。在大概 2006 年以前,JavaScript 被广泛的看作玩具语言和被用制作浏览器中好玩的动画,但是它里面隐藏着一些极其强大的特性。即 lambda 表达式中最重要的特性。人们开始暗中讨论一个叫做 “函数式编程的” 酷东西。 -[![](https://i.embed.ly/1/display/resize?url=https%3A%2F%2Fpbs.twimg.com%2Fprofile_images%2F3722564505%2Fb8030b8f3990875e8f38ed877fdf8d25_bigger.png&key=4fce0568f2ce49e8b54624ef71a8a5bd&width=40)](https://video.twimg.com/tweet_video/CmectVVVUAAsvpo.mp4) - ![](http://ww1.sinaimg.cn/large/006tNbRwgy1fekui0p6i3j30j50hcmyn.jpg) 我一直在告诉大家 #JavaScript 并不是一门玩具语言。现在我需要展示它。 From 09e5fb0658df415a4da89ce8b0fc8d68d6f5cee6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=B9=E5=8F=B7=E4=B8=89?= Date: Thu, 13 Apr 2017 10:35:19 +0800 Subject: [PATCH 148/638] Update the-rise-and-fall-and-rise-of-functional-programming-composable-software.md --- ...all-and-rise-of-functional-programming-composable-software.md | 1 + 1 file changed, 1 insertion(+) diff --git a/TODO/the-rise-and-fall-and-rise-of-functional-programming-composable-software.md b/TODO/the-rise-and-fall-and-rise-of-functional-programming-composable-software.md index 577d3d97d34..1b0a3b7c98c 100644 --- a/TODO/the-rise-and-fall-and-rise-of-functional-programming-composable-software.md +++ b/TODO/the-rise-and-fall-and-rise-of-functional-programming-composable-software.md @@ -89,6 +89,7 @@ lambda 表达式对软件设计产生了很大的影响,在 1980 年之前, 在 2010 年左右,一些有趣的事情发生了:JavaScript 的崛起。在大概 2006 年以前,JavaScript 被广泛的看作玩具语言和被用制作浏览器中好玩的动画,但是它里面隐藏着一些极其强大的特性。即 lambda 表达式中最重要的特性。人们开始暗中讨论一个叫做 “函数式编程的” 酷东西。 ![](http://ww1.sinaimg.cn/large/006tNbRwgy1fekui0p6i3j30j50hcmyn.jpg) + 我一直在告诉大家 #JavaScript 并不是一门玩具语言。现在我需要展示它。 在 2015 年,使用函数的组合来编写软件又开始流行起来。为了更简单化,JavaScript 规范获得的数十年来第一次主要的更新并且添加了箭头函数,为了更简单的编写和读取函数、柯里化,和 lambda 语句。 From 61ba93b7f49d64895f6a01c150acf9c57f1ff8b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=B9=E5=8F=B7=E4=B8=89?= Date: Thu, 13 Apr 2017 10:38:50 +0800 Subject: [PATCH 149/638] Update article link. --- ...ll-and-rise-of-functional-programming-composable-software.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO/the-rise-and-fall-and-rise-of-functional-programming-composable-software.md b/TODO/the-rise-and-fall-and-rise-of-functional-programming-composable-software.md index 1b0a3b7c98c..a529896a3c1 100644 --- a/TODO/the-rise-and-fall-and-rise-of-functional-programming-composable-software.md +++ b/TODO/the-rise-and-fall-and-rise-of-functional-programming-composable-software.md @@ -100,7 +100,7 @@ lambda 表达式对软件设计产生了很大的影响,在 1980 年之前, 你在阅读下一部分时,可以使用实例实验,记住要把你自己当孩子一样把其他的思想扔在一边在学习中去探索和玩耍。重新发现孩童时发现新事物的欣喜。让我们来做一些魔术吧。 -[接下来的第二部分:“为什么要用 JavaScript 学习函数式编程?”] +[接下来的第二部分:“为什么要用 JavaScript 学习函数式编程?”](https://github.com/xitu/gold-miner/blob/master/TODO/why-learn-functional-programming-in-javascript-composing-software.md) ### 下一步 From 4aa2181333f014def4d8f91742f5381eb0c788fc Mon Sep 17 00:00:00 2001 From: PhxNirvana Date: Thu, 13 Apr 2017 11:16:51 +0800 Subject: [PATCH 150/638] Update multithreading-with-rxjava-dadddc.md --- TODO/multithreading-with-rxjava-dadddc.md | 73 ++++++++++++----------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/TODO/multithreading-with-rxjava-dadddc.md b/TODO/multithreading-with-rxjava-dadddc.md index f2c11d0df96..d81eb058383 100644 --- a/TODO/multithreading-with-rxjava-dadddc.md +++ b/TODO/multithreading-with-rxjava-dadddc.md @@ -1,24 +1,25 @@ + > * 原文地址:[Multithreading with RxJava](https://android.jlelse.eu/multithreading-with-rxjava-dadddc4f7a63#.yghtx4u43) > * 原文作者:[Pierce Zaifman](https://android.jlelse.eu/@PierceZaifman?source=post_header_lockup) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 译者: +> * 译者:[PhxNirvana](https://github.com/phxnirvana) > * 校对者: -# Multithreading with RxJava # +# RxJava 中的多线程 # -Most of the code I’ve ever written for Android is fast enough that speed has never been a concern. Until a few weeks ago when I was working on an app that reads and analyzes large files. +大多数情况下,我写的 Android 代码都是可以流畅运行的,因此,速度从不是我考虑的问题。但几周前我处理一个读取和分析大文件的 app 时,浮现出了这一问题。 -While I expect users to understand that a larger file will take longer to process, at some point they will give up on my app. Either because they think the app isn’t working, or they simply aren’t willing to wait that long. So if I can cut that time in half or less, it’s certainly worth doing. +尽管我期望用户明白文件越大,耗时越长的道理,有时候他们仍会放弃我的应用。他们可能认为应用卡住了,也可能是因为他们就不想等那么久。所以如果我能把时间砍一半或更多的话,一定会大有裨意的。 -#### First attempt #### +#### 初体验 #### -Since I’ve been using RxJava for all of my background jobs, it made sense to try to do the same for this. Essentially I have some code that looks like this: +因为我所有后台任务都用 RxJava 重写了,所以继续用 RxJava 来解决这个问题也是自然而然的。尤其是我还有一些如下所示的代码: ``` List dataList; -//dataList gets populated here +//这里是数据列表 List result = new ArrayList<>(); for (String data : dataList) { @@ -26,80 +27,80 @@ for (String data : dataList) { } ``` -So, I just tried to stick each iteration of the loop into a background thread. You can see what I did below: +所以我只是想把循环的每个操作放到一个后台线程中。如下所示: ``` List dataList; -//dataList gets populated here +//这里是数据列表 List> tasks = new ArrayList<>(); for (String data : dataList) { tasks.add(Observable.just(data).subscribeOn(Schedulers.io()).map(s -> { - // returns a DataModel object + // 返回一个 DataModel 对象 return DataParser.createData(s); })); } List result = new ArrayList<>(); -// Wait for everything to finish and collect the results +// 等待运行结束并收集结果 for (DataModel dataModel : Observable.merge(tasks).toBlocking().toIterable()) { result.add(dataModel); } ``` -It did speed it up, it was running in about half the time. But, it was causing a lot of garbage collection (GC), which made the UI very stuttery and janky while it was loading. To try to understand what was happening, I added a log to print out `Thread.currentThread().getName()`. This made me realize that I was actually spawning a new thread for each piece of data. As it turns out, spawning thousands of threads is not a good idea. +的确起作用了,时间减少了近一半。但也导致大量垃圾回收(GC),这使得加载时的 UI 又卡又慢。为了搞清楚问题的原因,我加了一句 log 打印如下信息 `Thread.currentThread().getName()`。 这样我就搞清楚了,我在处理每一段数据时都新建了线程。正如结果所示,创建上千个线程并不是什么好主意。 -#### Second attempt #### +#### 二进宫 #### -I’ve already accomplished my goal of speeding up the data processing, but it’s not running very smoothly. I was also wondering if it could run even faster if it didn’t cause so much GC. So instead of spawning a new thread, I created my own limited thread pool for RxJava to use: +我已经完成了加速数据处理的目标,但运行起来并不那么流畅。我想知道如果不触发这么多 GC 的话还能不能跑得再快点。所以作为直接创建新线程的代替,我建立了我自己的限定线程数量的线程池提供给 RxJava 使用: ``` List dataList; -//dataList gets populated here +//这里是数据列表 List> tasks = new ArrayList<>(); -// Get the max number of threads we could have +// 取得能够使用的最大线程数 int threadCount = Runtime.getRuntime().availableProcessors(); ExecutorService threadPoolExecutor = Executors.newFixedThreadPool(threadCount); Scheduler scheduler = Schedulers.from(threadPoolExecutor); for (String data : dataList) { tasks.add(Observable.just(data).subscribeOn(scheduler).map(s -> { - // returns a DataModel object + // 返回一个 DataModel 对象 return DataParser.createData(s); })); } List result = new ArrayList<>(); -// Wait for everything to finish and collect the results +// 等待运行结束并收集结果 for (DataModel dataModel : Observable.merge(tasks).toBlocking().toIterable()) { result.add(dataModel); } ``` -For a dataset where each piece of data was quite large, this resulted in a further time savings of roughly 10%. However, for a dataset where each piece of data was small, it reduced the processing time by roughly 30%. It also reduced the number of GC calls, but there were still too many. +对于单个数据都很大的数据集来说,这样减少了约 10% 的数据处理时间。然而,对于单个数据都很小的数据集就减少了约 30% 的时间。这同时也减少了 GC 的数量,但 GC 还是太频繁。 #### Third attempt #### -I had one more idea — what if the overhead of managing and switching threads was slowing it down? To get around this, I could group my data into one sublist per thread. That way it would still run in parallel, but the management of each thread would be minimal. I adapted the solution [here](https://github.com/ReactiveX/RxJava/issues/3532#issuecomment-157509946) to implement this idea: +我有一个新想法——如果性能的瓶颈是线程的管理和切换会怎么样?为了克服这个问题,我可以将数据按照线程来拆分。这样虽然仍是并发运行,但对每个线程的管理将大大减小。我尝试使用 [这里](https://github.com/ReactiveX/RxJava/issues/3532#issuecomment-157509946) 的解决方法来实现我的想法: ``` List dataList; -//dataList gets populated here +//这里是数据列表 -// Get the max number of threads we could have +// 取得能够使用的最大线程数 int threadCount = Runtime.getRuntime().availableProcessors(); ExecutorService threadPoolExecutor = Executors.newFixedThreadPool(threadCount); Scheduler scheduler = Schedulers.from(threadPoolExecutor); AtomicInteger groupIndex = new AtomicInteger(); -// Split the data into groups by thread number, then process each group on their own thread +// 以线程数量为依据分组数据,将每组数据放到它们自己的线程中 Iterable> resultGroups = Observable.from(dataList).groupBy(k -> groupIndex.getAndIncrement() % threadCount) .flatMap(group -> group.observeOn(scheduler).toList().map(sublist -> { @@ -112,24 +113,24 @@ Iterable> resultGroups = List result = new ArrayList<>(); -// Wait for everything to finish and collect the results +// 等待运行结束并收集结果 for (List dataModels : resultGroups) { result.addAll(dataModels); } ``` -Previously I mentioned testing with two datasets. The first had large individual items but fewer total items. The second had many more total items, but each item was smaller. When I ran this solution against the first dataset, the difference was negligible. But, when I ran it on the second, longer dataset, the change was dramatic. With my previous approach it took about 20 seconds to process this dataset. Now, it takes about 5 seconds. +上文中我提到用两组数据测试,一组单个数据大但总数少,另一组单个数据小但数量多。当我再次测试时,第一组数据几乎没差别,而第二组改变相当大。之前几乎要 20秒,现在只需 5秒。 -The reason the second dataset was sped up so much, is because of the number of data items. For each item, it has to schedule a thread to do the work. Now that I’ve reduced this scheduling work, there’s very little overhead. +第二组数据运行时间改进如此大的原因是数量的改变。每个数据都需要指定工作线程,而现在减少了指定线程时的工作量,性能也从而提升了。 #### Clean up #### -There’s a few places in my code where I need to do this parallel work. So I refactored this solution into a util method that can be called anywhere: +上面的代码要执行并发还有一些地方需要修改,所以我重新用 util 整理了代码,增加了这段代码的通用性: ``` /** * Will process the data with the callable by splitting the data into the specified number of threads. - * T is ths type of data being parsed, and U is the type of data being returned. + * T 是要被处理的数据类型,U 是返回的数据类型 */ public static Iterable parseDataInParallel(List data, Func1, U> worker) { int threadCount = Runtime.getRuntime().availableProcessors(); @@ -161,20 +162,20 @@ for (List dataModels : resultGroups) { } ``` -Here `T` is the type being processed, in the example that would be `DataModel`. You pass in a `List` to be processed, and expect a result `U`. In my example `U` is `List`, but it could be anything, it doesn’t have to be a list either. The worker function being passed in is what does the processing of each sublist, and returns a result. +这里 `T` 是被处理的数据类型,样例中是`DataModel`。传入待处理的 `List` 并期望结果是 `U`。在我的样例中 `U` 是 `List`,但它可以是任何东西,并不一定是一个 list。被传入的工作方法处理每个子列表并返回结果。 -#### Can we make it faster? #### +#### 可以再快点么? #### -There are many factors at play here that affect how fast this can run. Like how the threads are managed, how many threads are available, what kind of device the app is running on, etc. Most of these I can’t control, but there was something else I didn’t consider with my solution. +事实上影响运行速度的因素有许多。比如线程管理方式,线程数,设备等。大多数因素我无法控制,但总有一些是我没有考虑到的。 -What if the data isn’t split evenly? For example, if I have 4 threads, what if every data item assigned to the 4th thread is 10 times larger than any other item? Then the 4th thread will end up running roughly 10 times longer than any other thread. In that case I won’t save much time with multithreading. The second approach I had actually solves this problem, since a thread is only assigned as it becomes available. But that solution was too slow. +如果数据不是平均分配的会怎么样?举个例子,如果有 4 个线程,第四个线程数据量是其他的 10 倍会怎么样?这时第四个线程的耗时就是其他线程的大约 10 倍。这种情况下使用多线程就不会减少多少时间。我的第二次尝试基本解决了这个问题,因为线程只在需要时才初始化。但这个方法太慢了。 -I also tried to change how the data is grouped into sublists. Instead of doing it arbitrarily, I could keep track of the total size of the data in each group. Then, assign each data item to whichever list is the smallest. This way, the amount of work done by each thread is close to being equal. Unfortunately, in testing this solution I found the time it takes to do this extra setup usually costs more time than it saves. +我也试过改变数据分组方式。作为随意分配的取代,我可以跟踪每一组数据的总量,然后将数据分配给最少的那组。这样每个线程的工作量就接近平均了。倒霉的是,测试之后发现这样做增加的时间远大于它节省的时间。 -Depending on how unevenly you expect your data to be split up, running it this way may actually be faster. But for the most part, it seems like just randomly splitting up the data is faster. Ideally if each thread could be assigned when available, with minimal overhead, it would be the most efficient solution. But, I couldn’t find a way to reduce the overhead enough to make it worth it. +这么做可能会更快,(结果)取决于原始数据有多不平均。但大多数情况下,随机分配看起来更快些。理想情况下是每个线程一有空就分配任务,同时执行分配所消耗的资源也少,这是最高效的。但我找不到一个足够高效的可以减少分配瓶颈的方法。 #### Final thoughts #### -So if you need to run some code in parallel, here’s one approach. Let me know what you think, there are many variables at play here. It’s difficult to determine the optimal solution, if there even is one. Also remember, just because you *can* run something in parallel doesn’t mean you *should*. +所以如果你想要并发运行一些代码,这里提供一种方法。让我知道你的想法,这里还有许多考验控制的变量。得到一个最优解(如果有的话)总是很难的。以及,**能**并发执行并不意味着**必须**用并发。 -### If You Enjoyed Reading, Please Click That Little Heart. If You Want To Read More Like This, Follow Me On [Medium](https://medium.com/@piercezaifman) . Thanks! ### +### 如果有收获的话,轻轻扎一下小红心吧老铁。想阅读更多,在 [Medium](https://medium.com/@piercezaifman) 关注我。谢谢!(顺便关注一下 [译者](https://juejin.im/user/57a16f4e6be3ff00650682d8) 233) ### From eb33e6ae1b32c8e99838d3a9195cdeba9759e8ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=94=85=E7=82=89=E5=B7=A5?= Date: Thu, 13 Apr 2017 12:50:46 +0800 Subject: [PATCH 151/638] modified according to @GitFuture 's revison --- ...ava-8-stream-android-rxjava2-hell-part4.md | 92 +++++-------------- 1 file changed, 21 insertions(+), 71 deletions(-) diff --git a/TODO/war-learning-curve-rx-java-2-java-8-stream-android-rxjava2-hell-part4.md b/TODO/war-learning-curve-rx-java-2-java-8-stream-android-rxjava2-hell-part4.md index b0b63eeb7c5..c496724c8e2 100644 --- a/TODO/war-learning-curve-rx-java-2-java-8-stream-android-rxjava2-hell-part4.md +++ b/TODO/war-learning-curve-rx-java-2-java-8-stream-android-rxjava2-hell-part4.md @@ -1,8 +1,8 @@ > * 原文地址:[War against Learning Curve of RxJava2 + Java8 Stream [ Android RxJava2 ] ( What the hell is this ) Part4](http://www.uwanttolearn.com/android/war-learning-curve-rx-java-2-java-8-stream-android-rxjava2-hell-part4/) > * 原文作者:[Hafiz Waleed Hussain](http://www.uwanttolearn.com/author/admin/) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 译者: -> * 校对者: +> * 译者: [Boiler Yao](https://github.com/boileryao) +> * 校对者: [Vivienmm](https://github.com/Vivienmm)、[GitFuture](https://github.com/GitFuture) ## 大战 RxJava2 和 Java8 Stream [ Android RxJava2 ] (这到底是什么) 第四部分 ## @@ -11,17 +11,16 @@ 又是新的一天,如果学点新东西,这一天一定会很酷炫。 -小伙伴们一切顺利啊,这是我们的 RxJava2 Android 系列的第四部分 [ [第一部分](https://github.com/xitu/gold-miner/blob/master/TODO/pull-vs-push-imperative-vs-reactive-reactive-programming-android-rxjava2-hell-part2.md), [第二部分](http://www.uwanttolearn.com/android/pull-vs-push-imperative-vs-reactive-reactive-programming-android-rxjava2-hell-part2/), [第三部分](https://github.com/xitu/gold-miner/blob/master/TODO/functional-interfaces-functional-programming-and-lambda-expressions-reactive-programming-android-rxjava2-what-the-hell-is-this-part3.md) ]。 好消息是我们已经做好准备,可以开始使用 Rx 了。在使用 RxJava2 Android Observable 之前,我会先用 Java8 的 Stream 来做响应式编程。我认为我们应该了解 Java8,我还感觉通过使用 Java8 的流式 API 会让 RxJava2 Android 的学习曲线更简单。 - +小伙伴们一切顺利啊,这是我们的 RxJava2 Android 系列的第四部分 [ [第一部分](https://github.com/xitu/gold-miner/blob/master/TODO/pull-vs-push-imperative-vs-reactive-reactive-programming-android-rxjava2-hell-part2.md), [第二部分](http://www.uwanttolearn.com/android/pull-vs-push-imperative-vs-reactive-reactive-programming-android-rxjava2-hell-part2/), [第三部分](https://github.com/xitu/gold-miner/blob/master/TODO/functional-interfaces-functional-programming-and-lambda-expressions-reactive-programming-android-rxjava2-what-the-hell-is-this-part3.md) ]。 好消息是我们已经做好准备,可以开始使用 Rx 了。在使用 RxJava2 Android Observable 之前,我会先用 Java8 的 Stream 来做响应式编程。我认为我们应该了解 Java8,而且通过使用 Java8 的 Stream API 让我感觉学习 RxJava2 Android 的过程更简单。 **动机:** -动机跟我在 [第一部分](https://github.com/xitu/gold-miner/blob/master/TODO/pull-vs-push-imperative-vs-reactive-reactive-programming-android-rxjava2-hell-part2.md) 和大家分享过的一样。在我开始学习 RxJava2 Android 的时候,我并不知道我该怎样使用、在什么地方使用它。 +动机跟我在 [第一部分](https://github.com/xitu/gold-miner/blob/master/TODO/pull-vs-push-imperative-vs-reactive-reactive-programming-android-rxjava2-hell-part2.md) 和大家分享过的一样。在我开始学习 RxJava2 Android 的时候,我并不知道自己会在什么地方,以何种方式使用到它。 -现在我们已经学会了一些预备知识,但当时我什么都不知道。我那时从学习如何从数据或对象创建 Observable 开始。然后学会当 Observable 数据上发生变化时,去调用哪些接口(或者可以叫做“回调”)。这在理论上很棒,但是当我付诸实践的时候,GG了。我发现很多理论上应该成立的模式在我去用的时候完全不起作用。对我来说最大的问题,是不能用响应或者函数式响应的思维思考问题。我有命令式编程和面向对象编程的背景,由于先入为主,所以对我来说理解会有些难。我一直在问这些问题:我该在哪里实现?我应该怎么实现?如果你跟着这篇文章看下来,我可以 100% 保证你会知道怎样把命令式代码转换到 Rx 代码,虽然写出来的 Rx 代码可能不是很好,但是你起码知道怎么开始。 +现在我们已经学会了一些预备知识,但当时我什么都不懂。因此我开始学习如何根据数据或对象创建 Observable 。然后知道了当 Observable 的数据发生变化时,应该调用哪些接口(或者可以叫做“回调”)。这在理论上很好,但是当我付诸实践的时候,却 GG 了。我发现很多理论上应该成立的模式在我去用的时候完全不起作用。对我来说最大的问题,是不能用响应或者函数式响应的思维思考问题。我熟悉命令式编程和面向对象编程,由于先入为主,所以对我来说理解响应式会有些难。我一直在问这些问题:我该在哪里实现?我应该怎么实现?如果你能坚持看完这篇文章,我可以 100% 保证你会知道怎样把命令式代码转换成 Rx 代码,虽然写出来的 Rx 代码不是最好的,但至少你知道该从哪里入手了。 **回顾:** -我想回顾之前三篇文章中我们提到过的所有概念 [ [第一部分](https://github.com/xitu/gold-miner/blob/master/TODO/pull-vs-push-imperative-vs-reactive-reactive-programming-android-rxjava2-hell-part2.md)、[第二部分](http://www.uwanttolearn.com/android/pull-vs-push-imperative-vs-reactive-reactive-programming-android-rxjava2-hell-part2/)、 [第三部分](https://github.com/xitu/gold-miner/blob/master/TODO/functional-interfaces-functional-programming-and-lambda-expressions-reactive-programming-android-rxjava2-what-the-hell-is-this-part3.md) ]。因为现在我们要用到这些概念了。在 [第一部分](https://github.com/xitu/gold-miner/blob/master/TODO/pull-vs-push-imperative-vs-reactive-reactive-programming-android-rxjava2-hell-part2.md) 我们学习了观察者模式; 在 [第二部分](http://www.uwanttolearn.com/android/pull-vs-push-imperative-vs-reactive-reactive-programming-android-rxjava2-hell-part2/) 学习了拉模式和推模式、命令式和响应式;在 [第三部分](https://github.com/xitu/gold-miner/blob/master/TODO/functional-interfaces-functional-programming-and-lambda-expressions-reactive-programming-android-rxjava2-what-the-hell-is-this-part3.md) 我们学习了函数式接口(Functional Interfaces)、 接口默认方法(Default Methods)、高阶函数(Higher Order Functions)、函数的副作用(Side Effects in Functions)、纯函数(Pure Functions)、Lambda 表达式和函数式编程。我在下面写了一些定义(很无聊的东西)。如果你早就记得的话,可以跳到下一部分。 +我想回顾之前三篇文章中我们提到过的所有概念 [ [第一部分](https://github.com/xitu/gold-miner/blob/master/TODO/pull-vs-push-imperative-vs-reactive-reactive-programming-android-rxjava2-hell-part2.md)、[第二部分](http://www.uwanttolearn.com/android/pull-vs-push-imperative-vs-reactive-reactive-programming-android-rxjava2-hell-part2/)、 [第三部分](https://github.com/xitu/gold-miner/blob/master/TODO/functional-interfaces-functional-programming-and-lambda-expressions-reactive-programming-android-rxjava2-what-the-hell-is-this-part3.md) ]。因为现在我们要用到这些概念了。在 [第一部分](https://github.com/xitu/gold-miner/blob/master/TODO/pull-vs-push-imperative-vs-reactive-reactive-programming-android-rxjava2-hell-part2.md) 我们学习了观察者模式; 在 [第二部分](http://www.uwanttolearn.com/android/pull-vs-push-imperative-vs-reactive-reactive-programming-android-rxjava2-hell-part2/) 学习了拉模式和推模式、命令式和响应式;在 [第三部分](https://github.com/xitu/gold-miner/blob/master/TODO/functional-interfaces-functional-programming-and-lambda-expressions-reactive-programming-android-rxjava2-what-the-hell-is-this-part3.md) 我们学习了函数式接口(Functional Interfaces)、 接口默认方法(Default Methods)、高阶函数(Higher Order Functions)、函数的副作用(Side Effects in Functions)、纯函数(Pure Functions)、Lambda 表达式和函数式编程。我在下面写了一些定义(很无聊的东西)。如果你清楚这些定义,可以跳到下一部分。 **函数式接口是只有一个抽象方法的接口。** **在 Java8 我们可以在接口中定义方法,这种方法叫做“默认方法”。** **至少有一个参数是函数的函数和返回类型为函数的函数称为高阶函数。** @@ -30,7 +29,7 @@ **简介:** -今天我们将向 RxJava 的学习曲线宣战。我确定在最后我们会取得胜利。 +今天我们将向 RxJava 的学习宣战。我确定在最后我们会取得胜利。 作战策略: @@ -54,7 +53,7 @@ Stream: 第一个问题:在英语中 Stream 是什么意思? -答案:一条很窄的小河,或者源源不断流动的液体、空气、气体。在编程的时候把数据转化成“流”的形式,比如我有一个字符串但是我想把它变成“流”来使用我需要干些什么,我需要创建一个机制,使这个字符串满足“源源不断流动的液体、空气、气体 {**或者数据**}”的定义。问题是,我们为什么想要自己的数据变成“流”呢,下面是个简单的例子。 +答案:一条很窄的小河,或者源源不断流动的液体、空气、气体。在编程的时候把数据转化成“流”的形式,比如我有一个字符串,但是我想把它变成“流”来使用的话我需要干些什么,我需要创建一个机制,使这个字符串满足“源源不断流动的液体、空气、气体 {**或者数据**}”的定义。问题是,我们为什么想要自己的数据变成“流”呢,下面是个简单的例子。 就像下面这幅图中画的那样,我有一杯混合着大大小小石子的蓝色的水。 @@ -90,9 +89,9 @@ Stream: 把水转换成水流后,我们做了很多事情。我先用一个过滤器去掉了大石子,然后用另一个过滤器去掉了小石子, 最后用一个转换器(map)把水的颜色从蓝色变成黑色。 -当我将数据转换成流时,我将在编程中得到同样的好处。现在,我将把这个例子转换成代码。我要显示的代码是真正的代码。可能示例代码不能工作,但我将要使用的操作符和API是真实的,我们将在后面的实例中使用。所以,同志们不要把关注点放在编译上。通过这个例子,我有一种感觉,我们将很容易地把握这些概念。在这个例子中,重要的一点是,我使用Java8 的 Stream API 而不是 Rx API。我不想让事情变困难,但稍后我也会使用 Rx。 +当我将数据转换成流时,我将在编程中得到同样的好处。现在,我将把这个例子转换成代码。我要显示的代码是真正的代码。可能示例代码不能工作,但我将要使用的操作符和 API 是真实的,我们将在后面的实例中使用。所以,同志们不要把关注点放在编译上。通过这个例子,我有一种感觉,我们将很容易地把握这些概念。在这个例子中,重要的一点是,我使用 Java8 的 Stream API 而不是 Rx API。我不想让事情变困难,但稍后我也会使用 Rx。 -图像中的水 和 代码中的水: +图像中的水 & 代码中的水: [![](http://www.uwanttolearn.com/wp-content/uploads/2017/03/war_against_learning_curve_of_rx_java_2_java_8_stream_1-300x253.jpg)](http://www.uwanttolearn.com/wp-content/uploads/2017/03/war_against_learning_curve_of_rx_java_2_java_8_stream_1.jpg) @@ -107,36 +106,22 @@ public static void main(String [] args){ ``` 输出: - water - water - big stone - water - water - small stone - water - small stone - small stone - water - water - water - water - water -图像中的水流 和 代码中的水流: +图像中的水流 & 代码中的水流: [![](http://www.uwanttolearn.com/wp-content/uploads/2017/03/war_against_learning_curve_of_rx_java_2_java_8_stream_2-237x300.jpg)](http://www.uwanttolearn.com/wp-content/uploads/2017/03/war_against_learning_curve_of_rx_java_2_java_8_stream_2.jpg) @@ -150,7 +135,7 @@ public static void main(String[] args) { //输出和上面那个一样 ``` -图像中的“大石子过滤器” 和 代码中的“大石子过滤器”: +图像中的“大石子过滤器” & 代码中的“大石子过滤器”: [![](http://www.uwanttolearn.com/wp-content/uploads/2017/03/war_against_learning_curve_of_rx_java_2_java_8_stream_3-300x252.jpg)](http://www.uwanttolearn.com/wp-content/uploads/2017/03/war_against_learning_curve_of_rx_java_2_java_8_stream_3.jpg) @@ -167,13 +152,13 @@ private static Predicate BigStoneFilter = new Predicate() { }; ``` -正如我们在 [第三部分](https://github.com/xitu/gold-miner/blob/master/TODO/functional-interfaces-functional-programming-and-lambda-expressions-reactive-programming-android-rxjava2-what-the-hell-is-this-part3.md) 已经知道的,任何函数式接口都可以转换成 Lambda 表达式。把上面的代码转换成 Lambda 表达式: +正如我们在 [第三部分](https://github.com/xitu/gold-miner/blob/master/TODO/functional-interfaces-functional-programming-and-lambda-expressions-reactive-programming-android-rxjava2-what-the-hell-is-this-part3.md) 所学到的,任何函数式接口都可以转换成 Lambda 表达式。把上面的代码转换成 Lambda 表达式: ``` private static Predicate BigStoneFilter = s -> !s.equals("big stone"); ``` -图像中和代码中作用在水流上的”大石子过滤器“: +图像和代码中的作用在水流上的“大石子过滤器”: [![](http://www.uwanttolearn.com/wp-content/uploads/2017/03/war_against_learning_curve_of_rx_java_2_java_8_stream_4-204x300.jpeg)](http://www.uwanttolearn.com/wp-content/uploads/2017/03/war_against_learning_curve_of_rx_java_2_java_8_stream_4.jpeg) @@ -189,36 +174,23 @@ private static Predicate BigStoneFilter = s -> !s.equals("big stone"); ``` 这里我使用了 forEach 方法,暂时把这当作流上的 for 循环。用在这里仅仅是为了输出。除去没有这个方法,我们也已经实现了我们在图像中表示的内容。是时候看看输出了: - water - water - water - water - small stone - water - small stone - small stone - water - water - water - water - water 没有大石子了,这意味着我们成功过滤了水。 -图像中的“小石子过滤器” 和 代码中的“小石子过滤器”: +图像中的“小石子过滤器” & 代码中的“小石子过滤器”: [![](http://www.uwanttolearn.com/wp-content/uploads/2017/03/war_against_learning_curve_of_rx_java_2_java_8_stream_5-300x229.jpg)](http://www.uwanttolearn.com/wp-content/uploads/2017/03/war_against_learning_curve_of_rx_java_2_java_8_stream_5.jpg) @@ -226,7 +198,7 @@ water private static Predicate SmallStoneFilter = s -> !s.equals("small stone"); ``` -在图像和代码中使用”小石子过滤器“: +在图像和代码中使用“小石子过滤器”: [![](http://www.uwanttolearn.com/wp-content/uploads/2017/03/war_against_learning_curve_of_rx_java_2_java_8_stream_6-228x300.png)](http://www.uwanttolearn.com/wp-content/uploads/2017/03/war_against_learning_curve_of_rx_java_2_java_8_stream_6.png) @@ -246,32 +218,23 @@ private static Predicate SmallStoneFilter = s -> !s.equals("small stone 我不打算解释 **SmallStoneFilter**,它的实现和 **BigStoneFilter** 是一样一样的。这里我只展示输出。 water - water - water - water - water - water - water - water - water - water -图像中的”水颜色转换器“ 和 代码中的”水颜色转换器“ +图像中的“水颜色转换器” 和 代码中的“水颜色转换器” [![](http://www.uwanttolearn.com/wp-content/uploads/2017/03/war_against_learning_curve_of_rx_java_2_java_8_stream_7-300x171.jpg)](http://www.uwanttolearn.com/wp-content/uploads/2017/03/war_against_learning_curve_of_rx_java_2_java_8_stream_7.jpg) 同志们这里需要注意! -在 Java8 Stream 中有个叫做 Function 的函数式接口。所以,当我想进行转换的时候,需要把这个函数式接口送到流的转换(map)函数里面。现在,我给大家展示在我们的代码中如何创建“水颜色过滤器”。 +在 Java8 Stream 中有个叫做 Function 的函数式接口。所以,当我想进行转换的时候,需要把这个函数式接口送到流的转换(map)函数里面。现在,我给大家展示在我们的代码中如何创建“水颜色转换器”。 ``` private static Function convertWaterColour = new Function() { @@ -316,25 +279,15 @@ private static Function convertWaterColour = s -> s + " black"; ``` 输出: - water black - water black - water black - water black - water black - water black - water black - water black - water black - water black 完活!现在我们再次回顾一些内容。 @@ -567,13 +520,9 @@ protected void onCreate(Bundle savedInstanceState) { ``` 输出: - 03-12 16:13:32.432 14918-14918/async.waleed.rx I/Android: 1 - 03-12 16:13:32.432 14918-14918/async.waleed.rx I/Android: 4 - 03-12 16:13:32.432 14918-14918/async.waleed.rx I/Android: 9 - 03-12 16:13:32.432 14918-14918/async.waleed.rx I/Android: 16 ``` @@ -605,7 +554,8 @@ Observable.fromArray(data) 我在一个项目中使用了 [OnBoarding](https://www.google.com/search?q=onboarding+ui&) 界面,根据 UI 设计需要在每个 OnBoarding 界面上显示点点,如下图所示: -[![](http://www.uwanttolearn.com/wp-content/uploads/2017/03/war_against_learning_curve_of_rx_java_2_java_8_stream_15-300x287.jpg)](http://www.uwanttolearn.com/wp-content/uploads/2017/03/war_against_learning_curve_of_rx_java_2_java_8_stream_15.jpg)如果你观察得很仔细的话,可以看到我需要将选定的界面对应的点设置成黑色。 +[![](http://www.uwanttolearn.com/wp-content/uploads/2017/03/war_against_learning_curve_of_rx_java_2_java_8_stream_15-300x287.jpg)](http://www.uwanttolearn.com/wp-content/uploads/2017/03/war_against_learning_curve_of_rx_java_2_java_8_stream_15.jpg) +如果你观察得很仔细的话,可以看到我需要将选定的界面对应的点设置成黑色。 命令式编程的代码: @@ -767,7 +717,7 @@ Observable.range(0, 10) **总结:** -同志们干得好!今天我们学 Rx Android 学得很开心。我们从图画开始,然后使用了 Java8 的流(Stream)。之后将 Java8 的流转换到 RxJava 2 Android 的 Observable。再之后,我们看到了真实项目中的示例并且展示了在现有的项目中如何开始使用 Rx。最后,我展示了一些转换到 Rx 的技巧:把循环用 forEach 替换,把 if 换成 filter,用 map 进行数据转化,用 flatmap 代替嵌套的循环。下篇文章: [Dialogue between Rx Observable and a Developer (Me) [ Android RxJava2 ] ( What the hell is this ) Part5](http://www.uwanttolearn.com/android/dialogue-rx-observable-developer-android-rxjava2-hell-part5/). +同志们干得好!今天我们学 Rx Android 学得很开心。我们从图画开始,然后使用了 Java8 的流(Stream)。之后将 Java8 的流转换到 RxJava 2 Android 的 Observable。再之后,我们看到了实际项目中的示例并且展示了在现有的项目中如何开始使用 Rx。最后,我展示了一些转换到 Rx 的技巧:把循环用 forEach 替换,把 if 换成 filter,用 map 进行数据转化,用 flatmap 代替嵌套的循环。下篇文章: [Dialogue between Rx Observable and a Developer (Me) [ Android RxJava2 ] ( What the hell is this ) Part5](http://www.uwanttolearn.com/android/dialogue-rx-observable-developer-android-rxjava2-hell-part5/). 希望你们开心,同志们再见! From f4f068d917286b48fc9ceb648b99c3a76304fb77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E6=99=93=E5=86=9B?= Date: Thu, 13 Apr 2017 14:18:42 +0800 Subject: [PATCH 152/638] reduce --- TODO/reduce-composing-software.md | 96 ++++++++++++++++--------------- 1 file changed, 49 insertions(+), 47 deletions(-) diff --git a/TODO/reduce-composing-software.md b/TODO/reduce-composing-software.md index 8baf2c381ad..312cc27ea78 100644 --- a/TODO/reduce-composing-software.md +++ b/TODO/reduce-composing-software.md @@ -4,18 +4,18 @@ > * 译者: > * 校对者: -# Reduce (Composing Software) # +# Reduce (软件编写) (第五部分)# -Smoke Art Cubes to Smoke — MattysFlicks — (CC BY 2.0) +Smoke Art Cubes to Smoke — MattysFlicks — (CC BY 2.0) (译注:该图是用 PS 将烟雾处理成方块状后得到的效果,参见 [flickr](https://www.flickr.com/photos/68397968@N07/11432696204)。)) -> Note: This is part of the “Composing Software” series on learning functional programming and compositional software techniques in JavaScript ES6+ from the ground up. Stay tuned. There’s a lot more of this to come! -> [< Previous](https://medium.com/javascript-scene/higher-order-functions-composing-software-5365cf2cbe99#.su6cmn4f7) | [<< Start over at Part 1](https://medium.com/javascript-scene/the-rise-and-fall-and-rise-of-functional-programming-composable-software-c2d91b424c8c#.2dfd6n6qe) | [Next >](https://medium.com/javascript-scene/functors-categories-61e031bac53f#.4hqndcx22) +> 注意:这是 “软件编写” 系列文章的第四部分,该系列主要阐述如何在 JavaScript ES6+ 中从零开始学习函数式编程和组合化软件(compositional software)技术(译注:关于软件可组合性的概念,参见维基百科 [Composability](https://en.wikipedia.org/wiki/Composability))。后续还有更多精彩内容,敬请期待! +> [<上一篇](https://medium.com/javascript-scene/reduce-composing-software-fe22f0c39a1d#.w4y0mlpcs) | [<< 返回第一章](https://medium.com/javascript-scene/the-rise-and-fall-and-rise-of-functional-programming-composable-software-c2d91b424c8c#.2dfd6n6qe) -**Reduce** (aka: fold, accumulate) utility commonly used in functional programming that lets you iterate over a list, applying a function to an accumulated value and the next item in the list, until the iteration is complete and the accumulated value gets returned. Many useful things can be implemented with reduce. Frequently, it’s the most elegant way to do any non-trivial processing on a collection of items. +在函数式编程中,**reduce**(也称为:fold,ccumulate)允许你在一个序列上迭代,并应用一个函数来处理预先声明的累积值和当前迭代到的元素。当迭代完成时,将返回这个累积值。许多其他有用的事物都可以通过 reduce 实现。多数时候,reduce 可以说是处理集合中元素最优雅的方式。 -Reduce takes a reducer function and an initial value, and returns the accumulated value. For `Array.prototype.reduce()`, the initial list is provided by `this`, so it's not one of the arguments: +reduce 接受一个 reducer 函数以及一个初始值,最终返回一个累积值。对于 `Array.prototype.reduce()` 来说, 初始列表将由 `this` 指明, 所以列表本身不会作为该函数的参数: ``` array.reduce( @@ -24,28 +24,28 @@ array.reduce( ) => accumulator: Any ``` -Let’s sum an array: +我们利用如下方式对一个数组进行求和: ``` [2, 4, 6].reduce((acc, n) => acc + n, 0); // 12 ``` -For each element in the array, the reducer is called and passed the accumulator and the current value. The reducer’s job is to “fold” the current value into the accumulated value somehow. How is not specified, and specifying how is the purpose of the reducer function. The reducer returns the new accumulated value, and `reduce()` moves on to the next value in the array. The reducer may need an initial value to start with, so most implementations take an initial value as a parameter. +对于数组的每步迭代,reducer 函数都会被调用,并且向其传入了累积值和当前迭代到的数组元素。reducer 的职责在于以某种方式将当前迭代的元素 “合拢(fold)” 到累加值中。reducer 规定了 “合拢” 的手段和方式,完成了对当前元素的 “合拢” 后,reducer 将返回新的累加值,然后, `.reduce()` 将开始处理数组中的下一个元素。reducer 需要一个初始值才能开始工作,所以绝大多数的 `.reduce()` 实现都需要接收一个初始值作为参数。 -In the case of this summing reducer, the first time the reducer is called, `acc` starts at `0` (the value we passed to `.reduce()` as the second parameter). The reducer returns `0` + `2` (`2` was the first element in the array), which is `2`. For the next call, `acc = 2, n = 4` and the reducer returns the result of `2 + 4` (`6`). In the last iteration, `acc = 6, n = 6`, and the reducer returns `12`. Since the iteration is finished, `.reduce()` returns the final accumulated value, `12`. +在求数组元素和一例中,reducer 函数第一次调用时,`acc` 将会以 `0` 值(该值是传入 `.reduce()` 方法的第二个参数)开始。然后,reducer 返回了 `0` + `2`(`2` 是数组的第一个元素), 也就是返回了 `2` 作为新的累积值。下一步,`acc = 2, n = 4` 传入了 reducer,reducer返回了 `2 + 4`(`6`)。在最后一步迭代中,`acc = 6, n = 6`, reducer 返回了 `12`。迭代完成,`.reduce()` 返回了最终的累积值 `12`。 -In this case, we passed in an anonymous reducing function, but we can abstract it and give it a name: +在这一例子中,我们传入了一个匿名函数作为 reducer,但是我们也可以抽象出每次求和的过程为一个具名函数,这使得我们代码的复用成都更高: ``` const summingReducer = (acc, n) => acc + n; [2, 4, 6].reduce(summingReducer, 0); // 12 ``` -Normally, `reduce()` works left to right. In JavaScript, we also have `[].reduceRight()`, which works right to left. In other words, if you applied `.reduceRight()` to `[2, 4, 6]`, the first iteration would use `6` as the first value for `n`, and it would work its way back and finish with `2`. +通常,`reduce` 的工作过程为由左向右。在 JavaScript 中,我们也有一个 `[].reduceRight()` (译注:[MDN -- Array.prototype.reduceRight()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/ReduceRight))方法来让 reduce 由右向左地工作。 具体说来,如果你对数组 `[2, 4, 6]` 应用 `.reduceRight()` ,第一个被迭代到的元素就将是 `6`,最后一个迭代到的元素就是 `2`。 -### Reduce is Versatile ### +### 无所不能的 reduce ### -Reduce is versatile. It’s easy to define `map()`, `filter()`, `forEach()` and lots of other interesting things using reduce: +别吃惊,reduce 确实无所不能,你所熟悉的 `map()`,`filter()`,`forEach()` 以及其他函数都可借助于 reduce 来创建。 **Map:** @@ -55,7 +55,7 @@ const map = (fn, arr) => arr.reduce((acc, item, index, arr) => { }, []); ``` -For map, our accumulated value is a new array with a new element for each value in the original array. The new values are generated by applying the passed in mapping function (`fn`) to each element in the `arr` argument. We accumulate the new array by calling `fn` with the current element, and concatenating the result to the accumulator array, `acc`. +对于 map 来说,我们的累积值就是一个新的数组对象,该数组对象中的每个元素都由原数组对应元素映射得到。累积数组中新的元素由传入 map 的映射函数(`fn`)所确定:对于当前迭代到的元素 `item`,我们通过 `fn` 计算出新的元素,并将其拼接入累加数组 `arr` 中。 **Filter:** @@ -65,37 +65,37 @@ const filter = (fn, arr) => arr.reduce((newArr, item) => { }, []); ``` -Filter works in much the same way as map, except that we take a predicate function and *conditionally* append the current value to the new array if the element passes the predicate check (`fn(item)` returns `true`). +filter 的工作方式与 map 类似,只不过原数组的元素只有通过一个真值检测函数(predicate function)才能被送入新的累积数组中。亦即,相较于 map,filter 是**有条件**地选择元素到累积数组中,并且不会改变元素的值。 -For each of the above examples, you have a list of data, iterate over that data applying some function and folding the results into an accumulated value. Lots of applications spring to mind. But what if *your data is a list of functions?* +上面几个例子,你处理的数据都是一些数值序列,你在数值序列上应用指定的函数迭代数据,并将结果合拢到累积值中。大多数应用都因此开始雏形初备,但是你想过这个问题没有:**假如你的序列是函数序列呢?** **Compose:** -Reduce is also a convenient way to compose functions. Remember function composition: If you want to apply the function `f` to the result of `g` of `x` i.e., the composition, `f . g`, you could use the following JavaScript: +reduce 也是实现函数组合的便捷渠道。假如你想用将函数 `g` 的输出作为函数 `f` 的输入,即组合这两个函数: `f . g`,那么你可以使用下面的 JavaScript 代码片,它没有任何的抽象: ``` f(g(x)) ``` -Reduce lets us abstract that process to work on any number of functions, so you could easily define a function that would represent: +reduce 让我们能抽象出函数组合过程,从而让你也能轻易地实现更多层次的函数组合: ``` f(g(h(x))) ``` -To make that happen, we’ll need to run reduce in reverse. That is, right-to-left, rather than left-to-right. Thankfully, JavaScript provides a `.reduceRight()` method: +由于函数组合是由右向左的,我们就要使用上面提到的 `.reduceRight()` 方法来抽象函数组合过程: ``` const compose = (...fns) => x => fns.reduceRight((v, f) => f(v), x); ``` -> Note: If JavaScript had not provided `[].reduceRight()`, you could still implement `reduceRight()` -- using `reduce()`. I'll leave it to adventurous readers to figure out how. +> 注意:如果 JavaScript 的版本没有提供 `[].reduceRight()`,你可以借助于 `reduce` 实现该方法。该实现留给读者自己思考。 **Pipe:** -`compose()` is great if you want to represent the composition from the inside-out -- that is, in the math notation sense. But what if you want to think of it as a sequence of events? +`compose()` 很好地描述了由内至外的组合过程,某种程度上,这是数学上的关于输入输出的组合。如果你想从事件发生顺序上来思考函数组合呢? -Imagine we want to add `1` to a number and then double it. With `compose(), that would be: +假设我们想要对一个数值加 `1`,然后对新得到的数值进行翻倍。如果是利用 `compose()`,就需要这么做: ``` const add1 = n => n + 1; @@ -110,15 +110,15 @@ add1ThenDouble(2); // 6 // ((2 + 1 = 3) * 2 = 6) ``` -See the problem? The first step is listed last, so in order to understand the sequence, you’ll need to start at the bottom of the list and work your way backwards to the top. +发现问题没有?第一步(加1操作)是 compose 序列上的最后一个元素,所以,`compose` 需要你自底向上地分析流程的执行。 -Or we can reduce left-to-right as you normally would, instead of right-to-left: +如果我们以由左向右的方式进行 reduce 来创建函数组合,以示区别,我们用 `pipe` 来描述新的组合方式: ``` const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x); ``` -Now you can write `add1ThenDouble()` like this: +现在,新的流程就可以这么撰写: ``` const add1ThenDouble = pipe( @@ -130,7 +130,7 @@ add1ThenDouble(2); // 6 // ((2 + 1 = 3) * 2 = 6) ``` -This is important because sometimes if you compose backwards, you get a different result: +你也看到了,在组合中,顺序是非常重要的,如果你调换了 `double` 和 `add1` 的顺序,你将得到截然不同的结果: ``` const doubleThenAdd1 = pipe( @@ -141,25 +141,25 @@ const doubleThenAdd1 = pipe( doubleThenAdd1(2); // 5 ``` -We’ll go into more details on `compose()` and `pipe()` later. What you should understand right now is that `reduce()` is a very powerful tool, and you really need to learn it. Just be aware that if you get very tricky with reduce, some people may have a hard time following along. +之后,我们还会讨论跟多的关于 `compose()` 和 `pipe()` 的细节。现在,你所要知道的只是,`reduce()` 是一个极为强大的工具,因此一定要掌握它。 如果在学习过程中遇到了挫折,也大可不必灰心,很多开发者都花了大量时间在 reduce 的学习上。 -### A Word on Redux ### +### Redux 中的 reduce ### -You may have heard the term “reducer” used to describe the important state update bits of Redux. As of this writing, Redux is the most popular state management library/architecture for web applications built using React and Angular (the latter via `ngrx/store`). +你可能听说过 “reducer” 这个术语被用在了 [Redux](https://github.com/reactjs/redux) 的状态更新中。这篇文章撰写之时,对于使用了 React 或者 Angular 进行构建的 web 应用来说,Redux 是最流行的状态管理库/架构(Angualar 中的类 Redux 管理是 ngrx/store )。 -Redux uses reducer functions to manage application state. A Redux-style reducer takes the current state and an action object and returns the new state: +Redux 使用了 reducer 函数来管理应用状态。一个 Redux 风格的 reducer 接收一个当前应用状态 `state` 和 和交互对象 `action` 作为参数(译注:当前状态就相当于累积值,而 action 就相当于目前处理的元素),处理完成后,返回一个新的应用状态: ``` reducer(state: Any, action: { type: String, payload: Any}) => newState: Any ``` -Redux has some reducer rules you need to keep in mind: +Redux 的一些 reducer 规则需要你牢记在心: -1. A reducer called with no parameters should return its valid initial state. -2. If the reducer isn’t going to handle the action type, it still needs to return the state. -3. Redux reducers **must be pure functions.** +1. 一个 reducer 如果进行了无参调用,它要返回它的初始状态。 +2. 如果 reducer 操纵的 action 没有声明类型,他要返回当前状态。 +3. 最最重要的是,Redux reducer 必须是纯函数。 -Let’s rewrite our summing reducer as a Redux-style reducer that reduces over action objects: +现在,我们以 Redux 风格重写上面的求和 reducer,该 reducer 的行为将由 action 类型决定: ``` const ADD_VALUE = 'ADD_VALUE'; @@ -175,7 +175,9 @@ const summingReducer = (state = 0, action = {}) => { }; ``` -The cool thing about Redux is that the reducers are just standard reducers that you can plug into any `reduce()` implementation which respects the reducer function signature, including `[].reduce()`. That means you can create an array of action objects and reduce over them to get a snapshot of state representing the same state you'd have if those same actions were dispatched to your store: +关于 Redux 的一个非常美妙的事儿就是,其 reducer 都是标准的 reducer (译注:即接收 `accumulator` 和 `current` 两个参数的 reducer ),这意味着你将 Redux 中的 reducer 插入到任何现有的 `reduce()` 实现中去,比如最常用的 `[].reduce()`。以此为例,我们可以创建一个 action 对象的数组,并对其进行 reduce 操作,传入 `reduce()` 的将是我们定义好的 `summingReducer`,据此,我们获得一个状态快照。之后,一旦对 Redux 中的状态树(store)分派了同样的 action 序列,那么一定能俘获到相同的状态快照: + +The cool thing about Redux is that the reducers are just standard reducers that you can plug into any `reduce()` implementation which respects the reducer function signature, including `[].reduce()`. ``` const actions = [ @@ -187,26 +189,26 @@ const actions = [ actions.reduce(summingReducer, 0); // 3 ``` -That makes unit testing Redux-style reducers a breeze. +这使得对 Redux 风格的 reducer 的单元测试变得极为容易。 + +### 总结 ### -### Conclusion ### +现在,你应该可以瞥见 recude 的强大甚至是无所不能了。它不只能帮助我们定义出 map 或者 filter,还是函数式编程中重要的工具,这个工具强大在它是一个基础工具,能够通过它构建出更多更强大的工具。 -You should be starting to see that reduce is an incredibly useful and versatile abstraction. It’s definitely a little trickier to understand than map or filter, but it is an essential tool in your functional programming utility belt — one you can use to make a lot of other great tools. +[**下一篇: Functors(函子) & Categories(范畴) >**](https://medium.com/javascript-scene/functors-categories-61e031bac53f#.4hqndcx22) -[**Next: Functors & Categories >**](https://medium.com/javascript-scene/functors-categories-61e031bac53f#.4hqndcx22) -### Next Steps ### +### 接下来 ### -Want to learn more about functional programming in JavaScript? +想学习更多 JavaScript 函数式编程吗? -[Learn JavaScript with Eric Elliott](http://ericelliottjs.com/product/lifetime-access-pass/). If you’re not a member, you’re missing out! +[跟着 Eric Elliott 学 Javacript](http://ericelliottjs.com/product/lifetime-access-pass/),机不可失时不再来! -[ -](https://ericelliottjs.com/product/lifetime-access-pass/) +[](https://ericelliottjs.com/product/lifetime-access-pass/) -***Eric Elliott*** is the author of [*“Programming JavaScript Applications”*](http://pjabook.com) (O’Reilly), and [*“Learn JavaScript with Eric Elliott”*](http://ericelliottjs.com/product/lifetime-access-pass/) . He has contributed to software experiences for **Adobe Systems, Zumba Fitness, The Wall Street Journal, ESPN, BBC, and top recording artists including Usher, Frank Ocean, Metallica**, and many more. +**Eric Elliott** 是 [**“编写 JavaScript 应用”**](http://pjabook.com) (O’Reilly) 以及 [**“跟着 Eric Elliott 学 Javascript”**](http://ericelliottjs.com/product/lifetime-access-pass/) 两书的作者。他为许多公司和组织作过贡献,例如 **Adobe Systems**、**Zumba Fitness**、**The Wall Street Journal**、**ESPN** 和 **BBC** 等 , 也是很多机构的顶级艺术家,包括但不限于 **Usher**、**Frank Ocean** 以及 **Metallica**。 -*He spends most of his time in the San Francisco Bay Area with the most beautiful woman in the world.* +大多数时间,他都在 San Francisco Bay Area,同这世上最美丽的女子在一起。 --- From 76137a1e95bcb09c9a9d061ce0a416e99a756ea9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E6=99=93=E5=86=9B?= Date: Thu, 13 Apr 2017 14:23:43 +0800 Subject: [PATCH 153/638] reset --- TODO/reduce-composing-software.md | 96 +++++++++++++++---------------- 1 file changed, 47 insertions(+), 49 deletions(-) diff --git a/TODO/reduce-composing-software.md b/TODO/reduce-composing-software.md index 312cc27ea78..85591dbfa4e 100644 --- a/TODO/reduce-composing-software.md +++ b/TODO/reduce-composing-software.md @@ -4,18 +4,18 @@ > * 译者: > * 校对者: -# Reduce (软件编写) (第五部分)# +# Reduce (Composing Software) # -Smoke Art Cubes to Smoke — MattysFlicks — (CC BY 2.0) (译注:该图是用 PS 将烟雾处理成方块状后得到的效果,参见 [flickr](https://www.flickr.com/photos/68397968@N07/11432696204)。)) +Smoke Art Cubes to Smoke — MattysFlicks — (CC BY 2.0) -> 注意:这是 “软件编写” 系列文章的第四部分,该系列主要阐述如何在 JavaScript ES6+ 中从零开始学习函数式编程和组合化软件(compositional software)技术(译注:关于软件可组合性的概念,参见维基百科 [Composability](https://en.wikipedia.org/wiki/Composability))。后续还有更多精彩内容,敬请期待! -> [<上一篇](https://medium.com/javascript-scene/reduce-composing-software-fe22f0c39a1d#.w4y0mlpcs) | [<< 返回第一章](https://medium.com/javascript-scene/the-rise-and-fall-and-rise-of-functional-programming-composable-software-c2d91b424c8c#.2dfd6n6qe) +> Note: This is part of the “Composing Software” series on learning functional programming and compositional software techniques in JavaScript ES6+ from the ground up. Stay tuned. There’s a lot more of this to come! +> [< Previous](https://medium.com/javascript-scene/higher-order-functions-composing-software-5365cf2cbe99#.su6cmn4f7) | [<< Start over at Part 1](https://medium.com/javascript-scene/the-rise-and-fall-and-rise-of-functional-programming-composable-software-c2d91b424c8c#.2dfd6n6qe) | [Next >](https://medium.com/javascript-scene/functors-categories-61e031bac53f#.4hqndcx22) -在函数式编程中,**reduce**(也称为:fold,ccumulate)允许你在一个序列上迭代,并应用一个函数来处理预先声明的累积值和当前迭代到的元素。当迭代完成时,将返回这个累积值。许多其他有用的事物都可以通过 reduce 实现。多数时候,reduce 可以说是处理集合中元素最优雅的方式。 +**Reduce** (aka: fold, accumulate) utility commonly used in functional programming that lets you iterate over a list, applying a function to an accumulated value and the next item in the list, until the iteration is complete and the accumulated value gets returned. Many useful things can be implemented with reduce. Frequently, it’s the most elegant way to do any non-trivial processing on a collection of items. -reduce 接受一个 reducer 函数以及一个初始值,最终返回一个累积值。对于 `Array.prototype.reduce()` 来说, 初始列表将由 `this` 指明, 所以列表本身不会作为该函数的参数: +Reduce takes a reducer function and an initial value, and returns the accumulated value. For `Array.prototype.reduce()`, the initial list is provided by `this`, so it's not one of the arguments: ``` array.reduce( @@ -24,28 +24,28 @@ array.reduce( ) => accumulator: Any ``` -我们利用如下方式对一个数组进行求和: +Let’s sum an array: ``` [2, 4, 6].reduce((acc, n) => acc + n, 0); // 12 ``` -对于数组的每步迭代,reducer 函数都会被调用,并且向其传入了累积值和当前迭代到的数组元素。reducer 的职责在于以某种方式将当前迭代的元素 “合拢(fold)” 到累加值中。reducer 规定了 “合拢” 的手段和方式,完成了对当前元素的 “合拢” 后,reducer 将返回新的累加值,然后, `.reduce()` 将开始处理数组中的下一个元素。reducer 需要一个初始值才能开始工作,所以绝大多数的 `.reduce()` 实现都需要接收一个初始值作为参数。 +For each element in the array, the reducer is called and passed the accumulator and the current value. The reducer’s job is to “fold” the current value into the accumulated value somehow. How is not specified, and specifying how is the purpose of the reducer function. The reducer returns the new accumulated value, and `reduce()` moves on to the next value in the array. The reducer may need an initial value to start with, so most implementations take an initial value as a parameter. -在求数组元素和一例中,reducer 函数第一次调用时,`acc` 将会以 `0` 值(该值是传入 `.reduce()` 方法的第二个参数)开始。然后,reducer 返回了 `0` + `2`(`2` 是数组的第一个元素), 也就是返回了 `2` 作为新的累积值。下一步,`acc = 2, n = 4` 传入了 reducer,reducer返回了 `2 + 4`(`6`)。在最后一步迭代中,`acc = 6, n = 6`, reducer 返回了 `12`。迭代完成,`.reduce()` 返回了最终的累积值 `12`。 +In the case of this summing reducer, the first time the reducer is called, `acc` starts at `0` (the value we passed to `.reduce()` as the second parameter). The reducer returns `0` + `2` (`2` was the first element in the array), which is `2`. For the next call, `acc = 2, n = 4` and the reducer returns the result of `2 + 4` (`6`). In the last iteration, `acc = 6, n = 6`, and the reducer returns `12`. Since the iteration is finished, `.reduce()` returns the final accumulated value, `12`. -在这一例子中,我们传入了一个匿名函数作为 reducer,但是我们也可以抽象出每次求和的过程为一个具名函数,这使得我们代码的复用成都更高: +In this case, we passed in an anonymous reducing function, but we can abstract it and give it a name: ``` const summingReducer = (acc, n) => acc + n; [2, 4, 6].reduce(summingReducer, 0); // 12 ``` -通常,`reduce` 的工作过程为由左向右。在 JavaScript 中,我们也有一个 `[].reduceRight()` (译注:[MDN -- Array.prototype.reduceRight()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/ReduceRight))方法来让 reduce 由右向左地工作。 具体说来,如果你对数组 `[2, 4, 6]` 应用 `.reduceRight()` ,第一个被迭代到的元素就将是 `6`,最后一个迭代到的元素就是 `2`。 +Normally, `reduce()` works left to right. In JavaScript, we also have `[].reduceRight()`, which works right to left. In other words, if you applied `.reduceRight()` to `[2, 4, 6]`, the first iteration would use `6` as the first value for `n`, and it would work its way back and finish with `2`. -### 无所不能的 reduce ### +### Reduce is Versatile ### -别吃惊,reduce 确实无所不能,你所熟悉的 `map()`,`filter()`,`forEach()` 以及其他函数都可借助于 reduce 来创建。 +Reduce is versatile. It’s easy to define `map()`, `filter()`, `forEach()` and lots of other interesting things using reduce: **Map:** @@ -55,7 +55,7 @@ const map = (fn, arr) => arr.reduce((acc, item, index, arr) => { }, []); ``` -对于 map 来说,我们的累积值就是一个新的数组对象,该数组对象中的每个元素都由原数组对应元素映射得到。累积数组中新的元素由传入 map 的映射函数(`fn`)所确定:对于当前迭代到的元素 `item`,我们通过 `fn` 计算出新的元素,并将其拼接入累加数组 `arr` 中。 +For map, our accumulated value is a new array with a new element for each value in the original array. The new values are generated by applying the passed in mapping function (`fn`) to each element in the `arr` argument. We accumulate the new array by calling `fn` with the current element, and concatenating the result to the accumulator array, `acc`. **Filter:** @@ -65,37 +65,37 @@ const filter = (fn, arr) => arr.reduce((newArr, item) => { }, []); ``` -filter 的工作方式与 map 类似,只不过原数组的元素只有通过一个真值检测函数(predicate function)才能被送入新的累积数组中。亦即,相较于 map,filter 是**有条件**地选择元素到累积数组中,并且不会改变元素的值。 +Filter works in much the same way as map, except that we take a predicate function and *conditionally* append the current value to the new array if the element passes the predicate check (`fn(item)` returns `true`). -上面几个例子,你处理的数据都是一些数值序列,你在数值序列上应用指定的函数迭代数据,并将结果合拢到累积值中。大多数应用都因此开始雏形初备,但是你想过这个问题没有:**假如你的序列是函数序列呢?** +For each of the above examples, you have a list of data, iterate over that data applying some function and folding the results into an accumulated value. Lots of applications spring to mind. But what if *your data is a list of functions?* **Compose:** -reduce 也是实现函数组合的便捷渠道。假如你想用将函数 `g` 的输出作为函数 `f` 的输入,即组合这两个函数: `f . g`,那么你可以使用下面的 JavaScript 代码片,它没有任何的抽象: +Reduce is also a convenient way to compose functions. Remember function composition: If you want to apply the function `f` to the result of `g` of `x` i.e., the composition, `f . g`, you could use the following JavaScript: ``` f(g(x)) ``` -reduce 让我们能抽象出函数组合过程,从而让你也能轻易地实现更多层次的函数组合: +Reduce lets us abstract that process to work on any number of functions, so you could easily define a function that would represent: ``` f(g(h(x))) ``` -由于函数组合是由右向左的,我们就要使用上面提到的 `.reduceRight()` 方法来抽象函数组合过程: +To make that happen, we’ll need to run reduce in reverse. That is, right-to-left, rather than left-to-right. Thankfully, JavaScript provides a `.reduceRight()` method: ``` const compose = (...fns) => x => fns.reduceRight((v, f) => f(v), x); ``` -> 注意:如果 JavaScript 的版本没有提供 `[].reduceRight()`,你可以借助于 `reduce` 实现该方法。该实现留给读者自己思考。 +> Note: If JavaScript had not provided `[].reduceRight()`, you could still implement `reduceRight()` -- using `reduce()`. I'll leave it to adventurous readers to figure out how. **Pipe:** -`compose()` 很好地描述了由内至外的组合过程,某种程度上,这是数学上的关于输入输出的组合。如果你想从事件发生顺序上来思考函数组合呢? +`compose()` is great if you want to represent the composition from the inside-out -- that is, in the math notation sense. But what if you want to think of it as a sequence of events? -假设我们想要对一个数值加 `1`,然后对新得到的数值进行翻倍。如果是利用 `compose()`,就需要这么做: +Imagine we want to add `1` to a number and then double it. With `compose(), that would be: ``` const add1 = n => n + 1; @@ -110,15 +110,15 @@ add1ThenDouble(2); // 6 // ((2 + 1 = 3) * 2 = 6) ``` -发现问题没有?第一步(加1操作)是 compose 序列上的最后一个元素,所以,`compose` 需要你自底向上地分析流程的执行。 +See the problem? The first step is listed last, so in order to understand the sequence, you’ll need to start at the bottom of the list and work your way backwards to the top. -如果我们以由左向右的方式进行 reduce 来创建函数组合,以示区别,我们用 `pipe` 来描述新的组合方式: +Or we can reduce left-to-right as you normally would, instead of right-to-left: ``` const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x); ``` -现在,新的流程就可以这么撰写: +Now you can write `add1ThenDouble()` like this: ``` const add1ThenDouble = pipe( @@ -130,7 +130,7 @@ add1ThenDouble(2); // 6 // ((2 + 1 = 3) * 2 = 6) ``` -你也看到了,在组合中,顺序是非常重要的,如果你调换了 `double` 和 `add1` 的顺序,你将得到截然不同的结果: +This is important because sometimes if you compose backwards, you get a different result: ``` const doubleThenAdd1 = pipe( @@ -141,25 +141,25 @@ const doubleThenAdd1 = pipe( doubleThenAdd1(2); // 5 ``` -之后,我们还会讨论跟多的关于 `compose()` 和 `pipe()` 的细节。现在,你所要知道的只是,`reduce()` 是一个极为强大的工具,因此一定要掌握它。 如果在学习过程中遇到了挫折,也大可不必灰心,很多开发者都花了大量时间在 reduce 的学习上。 +We’ll go into more details on `compose()` and `pipe()` later. What you should understand right now is that `reduce()` is a very powerful tool, and you really need to learn it. Just be aware that if you get very tricky with reduce, some people may have a hard time following along. -### Redux 中的 reduce ### +### A Word on Redux ### -你可能听说过 “reducer” 这个术语被用在了 [Redux](https://github.com/reactjs/redux) 的状态更新中。这篇文章撰写之时,对于使用了 React 或者 Angular 进行构建的 web 应用来说,Redux 是最流行的状态管理库/架构(Angualar 中的类 Redux 管理是 ngrx/store )。 +You may have heard the term “reducer” used to describe the important state update bits of Redux. As of this writing, Redux is the most popular state management library/architecture for web applications built using React and Angular (the latter via `ngrx/store`). -Redux 使用了 reducer 函数来管理应用状态。一个 Redux 风格的 reducer 接收一个当前应用状态 `state` 和 和交互对象 `action` 作为参数(译注:当前状态就相当于累积值,而 action 就相当于目前处理的元素),处理完成后,返回一个新的应用状态: +Redux uses reducer functions to manage application state. A Redux-style reducer takes the current state and an action object and returns the new state: ``` reducer(state: Any, action: { type: String, payload: Any}) => newState: Any ``` -Redux 的一些 reducer 规则需要你牢记在心: +Redux has some reducer rules you need to keep in mind: -1. 一个 reducer 如果进行了无参调用,它要返回它的初始状态。 -2. 如果 reducer 操纵的 action 没有声明类型,他要返回当前状态。 -3. 最最重要的是,Redux reducer 必须是纯函数。 +1. A reducer called with no parameters should return its valid initial state. +2. If the reducer isn’t going to handle the action type, it still needs to return the state. +3. Redux reducers **must be pure functions.** -现在,我们以 Redux 风格重写上面的求和 reducer,该 reducer 的行为将由 action 类型决定: +Let’s rewrite our summing reducer as a Redux-style reducer that reduces over action objects: ``` const ADD_VALUE = 'ADD_VALUE'; @@ -175,9 +175,7 @@ const summingReducer = (state = 0, action = {}) => { }; ``` -关于 Redux 的一个非常美妙的事儿就是,其 reducer 都是标准的 reducer (译注:即接收 `accumulator` 和 `current` 两个参数的 reducer ),这意味着你将 Redux 中的 reducer 插入到任何现有的 `reduce()` 实现中去,比如最常用的 `[].reduce()`。以此为例,我们可以创建一个 action 对象的数组,并对其进行 reduce 操作,传入 `reduce()` 的将是我们定义好的 `summingReducer`,据此,我们获得一个状态快照。之后,一旦对 Redux 中的状态树(store)分派了同样的 action 序列,那么一定能俘获到相同的状态快照: - -The cool thing about Redux is that the reducers are just standard reducers that you can plug into any `reduce()` implementation which respects the reducer function signature, including `[].reduce()`. +The cool thing about Redux is that the reducers are just standard reducers that you can plug into any `reduce()` implementation which respects the reducer function signature, including `[].reduce()`. That means you can create an array of action objects and reduce over them to get a snapshot of state representing the same state you'd have if those same actions were dispatched to your store: ``` const actions = [ @@ -189,26 +187,26 @@ const actions = [ actions.reduce(summingReducer, 0); // 3 ``` -这使得对 Redux 风格的 reducer 的单元测试变得极为容易。 - -### 总结 ### +That makes unit testing Redux-style reducers a breeze. -现在,你应该可以瞥见 recude 的强大甚至是无所不能了。它不只能帮助我们定义出 map 或者 filter,还是函数式编程中重要的工具,这个工具强大在它是一个基础工具,能够通过它构建出更多更强大的工具。 +### Conclusion ### -[**下一篇: Functors(函子) & Categories(范畴) >**](https://medium.com/javascript-scene/functors-categories-61e031bac53f#.4hqndcx22) +You should be starting to see that reduce is an incredibly useful and versatile abstraction. It’s definitely a little trickier to understand than map or filter, but it is an essential tool in your functional programming utility belt — one you can use to make a lot of other great tools. +[**Next: Functors & Categories >**](https://medium.com/javascript-scene/functors-categories-61e031bac53f#.4hqndcx22) -### 接下来 ### +### Next Steps ### -想学习更多 JavaScript 函数式编程吗? +Want to learn more about functional programming in JavaScript? -[跟着 Eric Elliott 学 Javacript](http://ericelliottjs.com/product/lifetime-access-pass/),机不可失时不再来! +[Learn JavaScript with Eric Elliott](http://ericelliottjs.com/product/lifetime-access-pass/). If you’re not a member, you’re missing out! -[](https://ericelliottjs.com/product/lifetime-access-pass/) +[ +](https://ericelliottjs.com/product/lifetime-access-pass/) -**Eric Elliott** 是 [**“编写 JavaScript 应用”**](http://pjabook.com) (O’Reilly) 以及 [**“跟着 Eric Elliott 学 Javascript”**](http://ericelliottjs.com/product/lifetime-access-pass/) 两书的作者。他为许多公司和组织作过贡献,例如 **Adobe Systems**、**Zumba Fitness**、**The Wall Street Journal**、**ESPN** 和 **BBC** 等 , 也是很多机构的顶级艺术家,包括但不限于 **Usher**、**Frank Ocean** 以及 **Metallica**。 +***Eric Elliott*** is the author of [*“Programming JavaScript Applications”*](http://pjabook.com) (O’Reilly), and [*“Learn JavaScript with Eric Elliott”*](http://ericelliottjs.com/product/lifetime-access-pass/) . He has contributed to software experiences for **Adobe Systems, Zumba Fitness, The Wall Street Journal, ESPN, BBC, and top recording artists including Usher, Frank Ocean, Metallica**, and many more. -大多数时间,他都在 San Francisco Bay Area,同这世上最美丽的女子在一起。 +*He spends most of his time in the San Francisco Bay Area with the most beautiful woman in the world.* --- From ce75287cbd1757f21d6ef2a84961df549a35d6b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E6=99=93=E5=86=9B?= Date: Thu, 13 Apr 2017 14:32:12 +0800 Subject: [PATCH 154/638] reduce --- TODO/reduce-composing-software.md | 98 ++++++++++++++++--------------- 1 file changed, 50 insertions(+), 48 deletions(-) diff --git a/TODO/reduce-composing-software.md b/TODO/reduce-composing-software.md index 8baf2c381ad..2c42b1116aa 100644 --- a/TODO/reduce-composing-software.md +++ b/TODO/reduce-composing-software.md @@ -1,21 +1,21 @@ > * 原文地址:[Reduce (Composing Software)(part 5)](https://medium.com/javascript-scene/reduce-composing-software-fe22f0c39a1d) > * 原文作者:[Eric Elliott](https://medium.com/@_ericelliott?source=post_header_lockup) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 译者: +> * 译者:[yoyoyohamapi](https://github.com/yoyoyohamapi) [reid3290](https://github.com/reid3290) > * 校对者: -# Reduce (Composing Software) # +# Reduce (软件编写) (第五部分) # -Smoke Art Cubes to Smoke — MattysFlicks — (CC BY 2.0) +Smoke Art Cubes to Smoke — MattysFlicks — (CC BY 2.0) (译注:该图是用 PS 将烟雾处理成方块状后得到的效果,参见 [flickr](https://www.flickr.com/photos/68397968@N07/11432696204)。)) -> Note: This is part of the “Composing Software” series on learning functional programming and compositional software techniques in JavaScript ES6+ from the ground up. Stay tuned. There’s a lot more of this to come! -> [< Previous](https://medium.com/javascript-scene/higher-order-functions-composing-software-5365cf2cbe99#.su6cmn4f7) | [<< Start over at Part 1](https://medium.com/javascript-scene/the-rise-and-fall-and-rise-of-functional-programming-composable-software-c2d91b424c8c#.2dfd6n6qe) | [Next >](https://medium.com/javascript-scene/functors-categories-61e031bac53f#.4hqndcx22) +> 注意:这是 “软件编写” 系列文章的第四部分,该系列主要阐述如何在 JavaScript ES6+ 中从零开始学习函数式编程和组合化软件(compositional software)技术(译注:关于软件可组合性的概念,参见维基百科 [Composability](https://en.wikipedia.org/wiki/Composability))。后续还有更多精彩内容,敬请期待! +> [<上一篇](https://medium.com/javascript-scene/reduce-composing-software-fe22f0c39a1d#.w4y0mlpcs) | [<< 返回第一章](https://medium.com/javascript-scene/the-rise-and-fall-and-rise-of-functional-programming-composable-software-c2d91b424c8c#.2dfd6n6qe) -**Reduce** (aka: fold, accumulate) utility commonly used in functional programming that lets you iterate over a list, applying a function to an accumulated value and the next item in the list, until the iteration is complete and the accumulated value gets returned. Many useful things can be implemented with reduce. Frequently, it’s the most elegant way to do any non-trivial processing on a collection of items. +在函数式编程中,**reduce**(也称为:fold,ccumulate)允许你在一个序列上迭代,并应用一个函数来处理预先声明的累积值和当前迭代到的元素。当迭代完成时,将返回这个累积值。许多其他有用的事物都可以通过 reduce 实现。多数时候,reduce 可以说是处理集合中元素最优雅的方式。 -Reduce takes a reducer function and an initial value, and returns the accumulated value. For `Array.prototype.reduce()`, the initial list is provided by `this`, so it's not one of the arguments: +reduce 接受一个 reducer 函数以及一个初始值,最终返回一个累积值。对于 `Array.prototype.reduce()` 来说, 初始列表将由 `this` 指明, 所以列表本身不会作为该函数的参数: ``` array.reduce( @@ -24,28 +24,28 @@ array.reduce( ) => accumulator: Any ``` -Let’s sum an array: +我们利用如下方式对一个数组进行求和: ``` [2, 4, 6].reduce((acc, n) => acc + n, 0); // 12 ``` -For each element in the array, the reducer is called and passed the accumulator and the current value. The reducer’s job is to “fold” the current value into the accumulated value somehow. How is not specified, and specifying how is the purpose of the reducer function. The reducer returns the new accumulated value, and `reduce()` moves on to the next value in the array. The reducer may need an initial value to start with, so most implementations take an initial value as a parameter. +对于数组的每步迭代,reducer 函数都会被调用,并且向其传入了累积值和当前迭代到的数组元素。reducer 的职责在于以某种方式将当前迭代的元素 “合拢(fold)” 到累加值中。reducer 规定了 “合拢” 的手段和方式,完成了对当前元素的 “合拢” 后,reducer 将返回新的累加值,然后, `.reduce()` 将开始处理数组中的下一个元素。reducer 需要一个初始值才能开始工作,所以绝大多数的 `.reduce()` 实现都需要接收一个初始值作为参数。 -In the case of this summing reducer, the first time the reducer is called, `acc` starts at `0` (the value we passed to `.reduce()` as the second parameter). The reducer returns `0` + `2` (`2` was the first element in the array), which is `2`. For the next call, `acc = 2, n = 4` and the reducer returns the result of `2 + 4` (`6`). In the last iteration, `acc = 6, n = 6`, and the reducer returns `12`. Since the iteration is finished, `.reduce()` returns the final accumulated value, `12`. +在求数组元素和一例中,reducer 函数第一次调用时,`acc` 将会以 `0` 值(该值是传入 `.reduce()` 方法的第二个参数)开始。然后,reducer 返回了 `0` + `2`(`2` 是数组的第一个元素), 也就是返回了 `2` 作为新的累积值。下一步,`acc = 2, n = 4` 传入了 reducer,reducer返回了 `2 + 4`(`6`)。在最后一步迭代中,`acc = 6, n = 6`, reducer 返回了 `12`。迭代完成,`.reduce()` 返回了最终的累积值 `12`。 -In this case, we passed in an anonymous reducing function, but we can abstract it and give it a name: +在这一例子中,我们传入了一个匿名函数作为 reducer,但是我们也可以抽象出每次求和的过程为一个具名函数,这使得我们代码的复用成都更高: ``` const summingReducer = (acc, n) => acc + n; [2, 4, 6].reduce(summingReducer, 0); // 12 ``` -Normally, `reduce()` works left to right. In JavaScript, we also have `[].reduceRight()`, which works right to left. In other words, if you applied `.reduceRight()` to `[2, 4, 6]`, the first iteration would use `6` as the first value for `n`, and it would work its way back and finish with `2`. +通常,`reduce` 的工作过程为由左向右。在 JavaScript 中,我们也有一个 `[].reduceRight()` (译注:[MDN -- Array.prototype.reduceRight()](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/ReduceRight))方法来让 reduce 由右向左地工作。 具体说来,如果你对数组 `[2, 4, 6]` 应用 `.reduceRight()` ,第一个被迭代到的元素就将是 `6`,最后一个迭代到的元素就是 `2`。 -### Reduce is Versatile ### +### 无所不能的 reduce ### -Reduce is versatile. It’s easy to define `map()`, `filter()`, `forEach()` and lots of other interesting things using reduce: +别吃惊,reduce 确实无所不能,你所熟悉的 `map()`,`filter()`,`forEach()` 以及其他函数都可借助于 reduce 来创建。 **Map:** @@ -55,7 +55,7 @@ const map = (fn, arr) => arr.reduce((acc, item, index, arr) => { }, []); ``` -For map, our accumulated value is a new array with a new element for each value in the original array. The new values are generated by applying the passed in mapping function (`fn`) to each element in the `arr` argument. We accumulate the new array by calling `fn` with the current element, and concatenating the result to the accumulator array, `acc`. +对于 map 来说,我们的累积值就是一个新的数组对象,该数组对象中的每个元素都由原数组对应元素映射得到。累积数组中新的元素由传入 map 的映射函数(`fn`)所确定:对于当前迭代到的元素 `item`,我们通过 `fn` 计算出新的元素,并将其拼接入累加数组 `arr` 中。 **Filter:** @@ -65,37 +65,37 @@ const filter = (fn, arr) => arr.reduce((newArr, item) => { }, []); ``` -Filter works in much the same way as map, except that we take a predicate function and *conditionally* append the current value to the new array if the element passes the predicate check (`fn(item)` returns `true`). +filter 的工作方式与 map 类似,只不过原数组的元素只有通过一个真值检测函数(predicate function)才能被送入新的累积数组中。亦即,相较于 map,filter 是**有条件**地选择元素到累积数组中,并且不会改变元素的值。 -For each of the above examples, you have a list of data, iterate over that data applying some function and folding the results into an accumulated value. Lots of applications spring to mind. But what if *your data is a list of functions?* +上面几个例子,你处理的数据都是一些数值序列,你在数值序列上应用指定的函数迭代数据,并将结果合拢到累积值中。大多数应用都因此开始雏形初备,但是你想过这个问题没有:**假如你的序列是函数序列呢?** **Compose:** -Reduce is also a convenient way to compose functions. Remember function composition: If you want to apply the function `f` to the result of `g` of `x` i.e., the composition, `f . g`, you could use the following JavaScript: +reduce 也是实现函数组合的便捷渠道。假如你想用将函数 `g` 的输出作为函数 `f` 的输入,即组合这两个函数: `f . g`,那么你可以使用下面的 JavaScript 代码片,它没有任何的抽象: ``` f(g(x)) ``` -Reduce lets us abstract that process to work on any number of functions, so you could easily define a function that would represent: +reduce 让我们能抽象出函数组合过程,从而让你也能轻易地实现更多层次的函数组合: ``` f(g(h(x))) ``` -To make that happen, we’ll need to run reduce in reverse. That is, right-to-left, rather than left-to-right. Thankfully, JavaScript provides a `.reduceRight()` method: +由于函数组合是由右向左的,我们就要使用上面提到的 `.reduceRight()` 方法来抽象函数组合过程: ``` const compose = (...fns) => x => fns.reduceRight((v, f) => f(v), x); ``` -> Note: If JavaScript had not provided `[].reduceRight()`, you could still implement `reduceRight()` -- using `reduce()`. I'll leave it to adventurous readers to figure out how. +> 注意:如果 JavaScript 的版本没有提供 `[].reduceRight()`,你可以借助于 `reduce` 实现该方法。该实现留给读者自己思考。 **Pipe:** -`compose()` is great if you want to represent the composition from the inside-out -- that is, in the math notation sense. But what if you want to think of it as a sequence of events? +`compose()` 很好地描述了由内至外的组合过程,某种程度上,这是数学上的关于输入输出的组合。如果你想从事件发生顺序上来思考函数组合呢? -Imagine we want to add `1` to a number and then double it. With `compose(), that would be: +假设我们想要对一个数值加 `1`,然后对新得到的数值进行翻倍。如果是利用 `compose()`,就需要这么做: ``` const add1 = n => n + 1; @@ -110,15 +110,15 @@ add1ThenDouble(2); // 6 // ((2 + 1 = 3) * 2 = 6) ``` -See the problem? The first step is listed last, so in order to understand the sequence, you’ll need to start at the bottom of the list and work your way backwards to the top. +发现问题没有?第一步(加1操作)是 compose 序列上的最后一个元素,所以,`compose` 需要你自底向上地分析流程的执行。 -Or we can reduce left-to-right as you normally would, instead of right-to-left: +如果我们以由左向右的方式进行 reduce 来创建函数组合,以示区别,我们用 `pipe` 来描述新的组合方式: ``` const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x); ``` -Now you can write `add1ThenDouble()` like this: +现在,新的流程就可以这么撰写: ``` const add1ThenDouble = pipe( @@ -130,7 +130,7 @@ add1ThenDouble(2); // 6 // ((2 + 1 = 3) * 2 = 6) ``` -This is important because sometimes if you compose backwards, you get a different result: +你也看到了,在组合中,顺序是非常重要的,如果你调换了 `double` 和 `add1` 的顺序,你将得到截然不同的结果: ``` const doubleThenAdd1 = pipe( @@ -141,25 +141,25 @@ const doubleThenAdd1 = pipe( doubleThenAdd1(2); // 5 ``` -We’ll go into more details on `compose()` and `pipe()` later. What you should understand right now is that `reduce()` is a very powerful tool, and you really need to learn it. Just be aware that if you get very tricky with reduce, some people may have a hard time following along. +之后,我们还会讨论跟多的关于 `compose()` 和 `pipe()` 的细节。现在,你所要知道的只是,`reduce()` 是一个极为强大的工具,因此一定要掌握它。 如果在学习过程中遇到了挫折,也大可不必灰心,很多开发者都花了大量时间在 reduce 的学习上。 -### A Word on Redux ### +### Redux 中的 reduce ### -You may have heard the term “reducer” used to describe the important state update bits of Redux. As of this writing, Redux is the most popular state management library/architecture for web applications built using React and Angular (the latter via `ngrx/store`). +你可能听说过 “reducer” 这个术语被用在了 [Redux](https://github.com/reactjs/redux) 的状态更新中。这篇文章撰写之时,对于使用了 React 或者 Angular 进行构建的 web 应用来说,Redux 是最流行的状态管理库/架构(Angualar 中的类 Redux 管理是 ngrx/store )。 -Redux uses reducer functions to manage application state. A Redux-style reducer takes the current state and an action object and returns the new state: +Redux 使用了 reducer 函数来管理应用状态。一个 Redux 风格的 reducer 接收一个当前应用状态 `state` 和 和交互对象 `action` 作为参数(译注:当前状态就相当于累积值,而 action 就相当于目前处理的元素),处理完成后,返回一个新的应用状态: ``` reducer(state: Any, action: { type: String, payload: Any}) => newState: Any ``` -Redux has some reducer rules you need to keep in mind: +Redux 的一些 reducer 规则需要你牢记在心: -1. A reducer called with no parameters should return its valid initial state. -2. If the reducer isn’t going to handle the action type, it still needs to return the state. -3. Redux reducers **must be pure functions.** +1. 一个 reducer 如果进行了无参调用,它要返回它的初始状态。 +2. 如果 reducer 操纵的 action 没有声明类型,他要返回当前状态。 +3. 最最重要的是,Redux reducer 必须是纯函数。 -Let’s rewrite our summing reducer as a Redux-style reducer that reduces over action objects: +现在,我们以 Redux 风格重写上面的求和 reducer,该 reducer 的行为将由 action 类型决定: ``` const ADD_VALUE = 'ADD_VALUE'; @@ -175,7 +175,9 @@ const summingReducer = (state = 0, action = {}) => { }; ``` -The cool thing about Redux is that the reducers are just standard reducers that you can plug into any `reduce()` implementation which respects the reducer function signature, including `[].reduce()`. That means you can create an array of action objects and reduce over them to get a snapshot of state representing the same state you'd have if those same actions were dispatched to your store: +关于 Redux 的一个非常美妙的事儿就是,其 reducer 都是标准的 reducer (译注:即接收 `accumulator` 和 `current` 两个参数的 reducer ),这意味着你将 Redux 中的 reducer 插入到任何现有的 `reduce()` 实现中去,比如最常用的 `[].reduce()`。以此为例,我们可以创建一个 action 对象的数组,并对其进行 reduce 操作,传入 `reduce()` 的将是我们定义好的 `summingReducer`,据此,我们获得一个状态快照。之后,一旦对 Redux 中的状态树(store)分派了同样的 action 序列,那么一定能俘获到相同的状态快照: + +The cool thing about Redux is that the reducers are just standard reducers that you can plug into any `reduce()` implementation which respects the reducer function signature, including `[].reduce()`. ``` const actions = [ @@ -187,26 +189,26 @@ const actions = [ actions.reduce(summingReducer, 0); // 3 ``` -That makes unit testing Redux-style reducers a breeze. +这使得对 Redux 风格的 reducer 的单元测试变得极为容易。 + +### 总结 ### -### Conclusion ### +现在,你应该可以瞥见 recude 的强大甚至是无所不能了。它不只能帮助我们定义出 map 或者 filter,还是函数式编程中重要的工具,这个工具强大在它是一个基础工具,能够通过它构建出更多更强大的工具。 -You should be starting to see that reduce is an incredibly useful and versatile abstraction. It’s definitely a little trickier to understand than map or filter, but it is an essential tool in your functional programming utility belt — one you can use to make a lot of other great tools. +[**下一篇: Functors(函子) & Categories(范畴) >**](https://medium.com/javascript-scene/functors-categories-61e031bac53f#.4hqndcx22) -[**Next: Functors & Categories >**](https://medium.com/javascript-scene/functors-categories-61e031bac53f#.4hqndcx22) -### Next Steps ### +### 接下来 ### -Want to learn more about functional programming in JavaScript? +想学习更多 JavaScript 函数式编程吗? -[Learn JavaScript with Eric Elliott](http://ericelliottjs.com/product/lifetime-access-pass/). If you’re not a member, you’re missing out! +[跟着 Eric Elliott 学 Javacript](http://ericelliottjs.com/product/lifetime-access-pass/),机不可失时不再来! -[ -](https://ericelliottjs.com/product/lifetime-access-pass/) +[](https://ericelliottjs.com/product/lifetime-access-pass/) -***Eric Elliott*** is the author of [*“Programming JavaScript Applications”*](http://pjabook.com) (O’Reilly), and [*“Learn JavaScript with Eric Elliott”*](http://ericelliottjs.com/product/lifetime-access-pass/) . He has contributed to software experiences for **Adobe Systems, Zumba Fitness, The Wall Street Journal, ESPN, BBC, and top recording artists including Usher, Frank Ocean, Metallica**, and many more. +**Eric Elliott** 是 [**“编写 JavaScript 应用”**](http://pjabook.com) (O’Reilly) 以及 [**“跟着 Eric Elliott 学 Javascript”**](http://ericelliottjs.com/product/lifetime-access-pass/) 两书的作者。他为许多公司和组织作过贡献,例如 **Adobe Systems**、**Zumba Fitness**、**The Wall Street Journal**、**ESPN** 和 **BBC** 等 , 也是很多机构的顶级艺术家,包括但不限于 **Usher**、**Frank Ocean** 以及 **Metallica**。 -*He spends most of his time in the San Francisco Bay Area with the most beautiful woman in the world.* +大多数时间,他都在 San Francisco Bay Area,同这世上最美丽的女子在一起。 --- From c273b62d7998d0e0fd49332aa5d841010ded0763 Mon Sep 17 00:00:00 2001 From: sqrthree Date: Thu, 13 Apr 2017 22:52:38 +0800 Subject: [PATCH 155/638] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20=E5=86=99=E7=BB=99?= =?UTF-8?q?=E2=80=9C=E8=80=81=E6=B4=BE=E2=80=9D=20Web=20=E5=BC=80=E5=8F=91?= =?UTF-8?q?=E8=80=85=E7=9A=84=E2=80=9C=E7=8E=B0=E4=BB=A3=E2=80=9D=20JavaSc?= =?UTF-8?q?ript=20=E6=8C=87=E5=8D=97=20=E7=9A=84=E7=A7=AF=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 5 ++--- front-end.md | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 54648b75c5d..255e14d40e7 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,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) 上的英文分享文章。内容覆盖 [Android](#android)、[iOS](#ios)、[React](#react)、[前端](#前端)、[后端](#后端)、[产品](#产品)、[设计](#设计) 等领域,读者为热爱新技术的新锐开发者。 -掘金翻译计划目前翻译完成 [453](#近期文章列表) 篇文章,共有 [300](https://github.com/xitu/gold-miner/wiki/%E8%AF%91%E8%80%85%E7%A7%AF%E5%88%86%E8%A1%A8) 余名译者贡献翻译。 +掘金翻译计划目前翻译完成 [454](#近期文章列表) 篇文章,共有 [300](https://github.com/xitu/gold-miner/wiki/%E8%AF%91%E8%80%85%E7%A7%AF%E5%88%86%E8%A1%A8) 余名译者贡献翻译。 # 官方指南: @@ -37,11 +37,10 @@ ## 前端 +* [写给“老派” Web 开发者的“现代” JavaScript 指南](https://juejin.im/post/58ebab0c8d6d81006191376f?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([SunCafe](http://suncafe.cc/) 翻译) * [CSS很棒,只是真的太难了](https://juejin.im/entry/58eae24a61ff4b0061a6a102?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([ZhangFe](https://github.com/ZhangFe) 翻译) * [Preload,Prefetch 和它们在 Chrome 之中的优先级](https://juejin.im/post/58e8acf10ce46300585a7a42?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([gy134340](http://gy134340.com/) 翻译) * [setState() 门事件](https://juejin.im/post/58e5aeccb123db15eb80ecb0?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([reid3290](https://github.com/reid3290) 翻译) -* [如何使用 JavaScript 构建响应式引擎 —— Part 2:计算属性和依赖追踪](https://juejin.im/post/58ddeb1a570c3500579016ef?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([IridescentMia](https://github.com/IridescentMia) 翻译) -* [如何使用 JavaScript 构建响应式引擎 —— Part 1:可观察的对象](https://juejin.im/post/58dc9da661ff4b0061547ca0?utm_source=gold-miner&utm_medium=readme&utm_campaign=github)([IridescentMia](https://github.com/IridescentMia) 翻译) * [所有前端译文>>](https://github.com/xitu/gold-miner/blob/master/front-end.md) diff --git a/front-end.md b/front-end.md index 52e31749ac5..bcbc316bce0 100644 --- a/front-end.md +++ b/front-end.md @@ -1,3 +1,4 @@ +* [写给“老派” Web 开发者的“现代” JavaScript 指南](https://juejin.im/post/58ebab0c8d6d81006191376f?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([SunCafe](http://suncafe.cc/) 翻译) * [CSS很棒,只是真的太难了](https://juejin.im/entry/58eae24a61ff4b0061a6a102?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([ZhangFe](https://github.com/ZhangFe) 翻译) * [Preload,Prefetch 和它们在 Chrome 之中的优先级](https://juejin.im/post/58e8acf10ce46300585a7a42?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([gy134340](http://gy134340.com/) 翻译) * [setState() 门事件](https://juejin.im/post/58e5aeccb123db15eb80ecb0?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([reid3290](https://github.com/reid3290) 翻译) From 30f822f51a7b1b0d7c03045e84f24c87d700d648 Mon Sep 17 00:00:00 2001 From: sqrthree Date: Thu, 13 Apr 2017 23:01:01 +0800 Subject: [PATCH 156/638] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20webpack=20?= =?UTF-8?q?=E6=8B=BE=E7=BF=A0=EF=BC=9A=E5=85=85=E5=88=86=E5=88=A9=E7=94=A8?= =?UTF-8?q?=20CommonsChunkPlugin()=20=E7=9A=84=E7=A7=AF=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- front-end.md | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 255e14d40e7..0179de489e5 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,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) 上的英文分享文章。内容覆盖 [Android](#android)、[iOS](#ios)、[React](#react)、[前端](#前端)、[后端](#后端)、[产品](#产品)、[设计](#设计) 等领域,读者为热爱新技术的新锐开发者。 -掘金翻译计划目前翻译完成 [454](#近期文章列表) 篇文章,共有 [300](https://github.com/xitu/gold-miner/wiki/%E8%AF%91%E8%80%85%E7%A7%AF%E5%88%86%E8%A1%A8) 余名译者贡献翻译。 +掘金翻译计划目前翻译完成 [455](#近期文章列表) 篇文章,共有 [300](https://github.com/xitu/gold-miner/wiki/%E8%AF%91%E8%80%85%E7%A7%AF%E5%88%86%E8%A1%A8) 余名译者贡献翻译。 # 官方指南: @@ -37,10 +37,10 @@ ## 前端 +* [webpack 拾翠:充分利用 CommonsChunkPlugin()](https://juejin.im/post/58ec4e3f5c497d0062c470bf?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([reid3290](https://github.com/reid3290) 翻译) * [写给“老派” Web 开发者的“现代” JavaScript 指南](https://juejin.im/post/58ebab0c8d6d81006191376f?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([SunCafe](http://suncafe.cc/) 翻译) * [CSS很棒,只是真的太难了](https://juejin.im/entry/58eae24a61ff4b0061a6a102?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([ZhangFe](https://github.com/ZhangFe) 翻译) * [Preload,Prefetch 和它们在 Chrome 之中的优先级](https://juejin.im/post/58e8acf10ce46300585a7a42?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([gy134340](http://gy134340.com/) 翻译) -* [setState() 门事件](https://juejin.im/post/58e5aeccb123db15eb80ecb0?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([reid3290](https://github.com/reid3290) 翻译) * [所有前端译文>>](https://github.com/xitu/gold-miner/blob/master/front-end.md) diff --git a/front-end.md b/front-end.md index bcbc316bce0..1d501a043c1 100644 --- a/front-end.md +++ b/front-end.md @@ -1,3 +1,4 @@ +* [webpack 拾翠:充分利用 CommonsChunkPlugin()](https://juejin.im/post/58ec4e3f5c497d0062c470bf?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([reid3290](https://github.com/reid3290) 翻译) * [写给“老派” Web 开发者的“现代” JavaScript 指南](https://juejin.im/post/58ebab0c8d6d81006191376f?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([SunCafe](http://suncafe.cc/) 翻译) * [CSS很棒,只是真的太难了](https://juejin.im/entry/58eae24a61ff4b0061a6a102?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([ZhangFe](https://github.com/ZhangFe) 翻译) * [Preload,Prefetch 和它们在 Chrome 之中的优先级](https://juejin.im/post/58e8acf10ce46300585a7a42?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([gy134340](http://gy134340.com/) 翻译) From 06127d45b22e9bdd043a5b082064c8354000ef57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=B9=E5=8F=B7=E4=B8=89?= Date: Thu, 13 Apr 2017 23:05:54 +0800 Subject: [PATCH 157/638] Create debugging-tips-tricks.md --- TODO/debugging-tips-tricks.md | 229 ++++++++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+) create mode 100644 TODO/debugging-tips-tricks.md diff --git a/TODO/debugging-tips-tricks.md b/TODO/debugging-tips-tricks.md new file mode 100644 index 00000000000..dca2d0b0200 --- /dev/null +++ b/TODO/debugging-tips-tricks.md @@ -0,0 +1,229 @@ +> * 原文地址:[Debugging Tips and Tricks](https://css-tricks.com/debugging-tips-tricks/) +> * 原文作者:[SARAH DRASNER](https://css-tricks.com/author/sdrasner/) +> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) +> * 译者: +> * 校对者: + +# Debugging Tips and Tricks # + +Writing code is only one small piece of being a developer. In order to be efficient and capable at our jobs, we must also excel at debugging. When I dedicate some time to learning new debugging skills, I often find I can move much quicker, and add more value to the teams I work on. I have a few tips and tricks I rely on pretty heavily and found that I give the same advice again and again during workshops, so here’s a compilation of some of them, as well as some from the community. We'll start with some core tenants and then drill down to more specific examples. + +[![](https://s3.buysellads.com/1279518/7386844-1491489478.jpg)](https://srv.buysellads.com/ads/click/x/GTND4237CVYDL23JCV74YKQWF67DTKJNCT7DKZ3JCEAI45QUCT7D6K7KC6BDLKJEF67DTK3EHJNCLSIZ) + + +### # Main Concepts ### + +#### # Isolate the Problem #### + +Isolation is possibly the strongest core tenant in all of debugging. Our codebases can be sprawling, with different libraries, frameworks, and they can include many contributors, even people who aren't working on the project anymore. Isolating the problem helps us slowly whittle away non-essential parts of the problem so that we can singularly focus on a solution. + +Some of the benefits of isolation include, but are not limited to: + +- Figuring out if it’s actually the root cause we think it is or some sort of conflict +- For time-based tasks, understanding whether or not there is a race condition +- Taking a hard look at whether or not our code can be simplified, which can help with both writing it and maintaining it +- Untangling it and seeing if it’s one issue or perhaps more + +It's very important to make the issue reproducible. Without being able to discern exactly what the issue is in a way where you can reproduce it, it’s very difficult to solve for it. This also allows you to compare it to working model that is similar so that you can see what changed or what is different between the two. + +I have a lot of different methods of isolation in practice. One is to create a reduced test case on a local instance, or a private CodePen, or a JSBin. Another is to create breakpoints in the code so that I can see it execute bit by bit. There are a few ways to define breakpoints: + +You can literally write `debugger;` inline in your code. You can see how this will fire small pieces at a time. + +You can take this one step further in Chrome DevTools and even walk through the next events that are fired or choose specific event listeners: + +![Step into the next function call](https://cdn.css-tricks.com/wp-content/uploads/2017/04/stepintonextfunctioncall.gif) + +Good 'ol `console.log` is a form of isolation. (Or `echo` in PHP, or `print` in python, etc…). You are taking one tiny piece of execution and testing your assumptions, or checking to see if something is altering. This is probably the most time-tested form of debugging, that no matter how advanced you become, still has it's uses. Arrow functions in ES6 have allowed us to step up our console debugging game as well, as it’s now a lot easier to write useful one-liners in the console. + +The `console.table` function is also a favorite tool of mine, especially great for when you have a lot of data you need to represent- large arrays, large objects and the like. The `console.dir` function is also a nice alternative. It will log an interactive listing of an object's properties. + +![](https://cdn.css-tricks.com/wp-content/uploads/2017/04/dir.png) + +console.dir gives an interactive listing + +#### #Be Methodical #### + +When I teach workshops and help students in my class, the number one thing that I find holds them back as they try to debug a problem is not being methodical enough. This is truly a tortoise-and-the-hare kind of situation. They understandably want to move quickly, so they change a ton of things at once, and then when something stops working, they don’t know which thing they changed is causing the error. Then, to debug, they change many things at once and get a little lost trying to figure out what is working and what isn't. + +We all do this to some extent. As we become more proficient with a tool, we can write more and more code without testing an assumption. But if you’re new to a syntax or technology, being slow and careful behooves you. You have a much better shot at backing out of an issue you might have inadvertently created for yourself. And indeed, once you have created an issue, debugging one thing at a time might seem slower, but exposes exactly what changes have happened and where the error lies in a way that a *seemingly* faster pace doesn’t allow. I say seemingly because the time isn’t actually recovered working this way. + +**Do you remember when you were a kid and your parents said, "if you get lost, stay where you are?"** My parents did, at least. It's because if they were moving around to find me and I was also moving around to find them, we'd have fewer chances of bumping into one another. Code works the same way. The less moving pieces you have, the better- the more you are returning consistent results, the easier it will be to track things down. So while you’re debugging, try not to also install anything, or put in new dependencies. If you see a different error every time you should be returning a static result, that’s a big red flag you should be headed right for with your sleuth hat on. + +### # Choose Good Tools ### + +There are a million different tools for solving a variety of problems. I’m going to work through some of the tools I find the most useful and then we’ll link off to a bevy of resources. + +#### # Syntax Highlighting #### + +Sure, it’s damn fun to pick out the new hotness in colors and flavors for your syntax highlighting theme, but spending some time thinking about clarity here matters. I often pick dark themes where a skip in syntax will turn all of my code a lighter color, I find errors are really easy to see right away. I tend to like Oceanic Next or Panda, but really, to each their own on this one. It’s important to keep in mind that when looking for a good syntax highlighter, awesome-looking is great, but functional for calling out your mistakes is most important, and it's totally possible to do both. + +#### # Linting #### + +Linting helps flag suspicious code and calls out errors we might have overlooked. Linting is incredibly important, but which linter you choose has so much to do with what language/framework you’re writing in, and then on top of that, what your agreed-upon code style is. + +Different companies have different code styles and rules. Personally, I like [AirBnB's](https://github.com/airbnb/javascript), but take care and don't just use any old linter. Your linter enforces patterns that, if you yourself don’t want to enforce, can stall your build process. I had a CSS linter that complained whenever I wrote a browser hack, and ended up having to circumvent it so often that it stopped being useful. But a good linter can shine light on small errors you might have missed that are lurking. + +Here are some resources: + +- I recently found [this responsive images linter](https://github.com/ausi/respimagelint), that tells you what opportunities you might have to use picture, srcset, or sizes. +- Here’s a [pretty good breakdown](https://www.sitepoint.com/comparison-javascript-linting-tools/) of some JS linters + +#### # Browser Extensions #### + +Extensions can be really awesome because they can be enabled and disabled so readily, and they can work with really specific requirements. If you’re working with a particular library or framework, chances are, having their extension for DevTools enabled is going to give you all sorts of clarity that you can’t find otherwise. Take care though- not only can extensions bog a browser down, but they have permissions to execute scripts, so do a little homework into the extension author, ratings, and background. All that said, here are some of my favorites: + +- [Accessibility extension](https://chrome.google.com/webstore/detail/axe/lhdoppojpmngadmnindnejefpokejbdd) from Dequeue Systems +- [React DevTools](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi) really vital, in my opinion, if you're working with React, to see their virtual DOM +- [Vue DevTools](https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd) same endorsement as above. +- [Codopen](https://chrome.google.com/webstore/detail/codopen/agnkphdgffianchpipdbkeaclfbobaak): pops you out of the editor mode into a debug window for CodePen. Full disclosure: my husband made this for me as a present because he was sick of watching me manually opening the debug window (best gift ever!) +- [Pageruler](https://chrome.google.com/webstore/detail/page-ruler/jlpkojjdgbllmedoapgfodplfhcbnbpn): get pixel dimensions and measure anything on a page. I like this one because I’m super duper anal about my layout. This helps me feed the beast. + +### # DevTools ### + +This is probably the most obvious of debugging tools, and there are so many things you can do with them. They can have so many packed-in features that can be easy to miss, so in the next section of specific tips, we'll go into a deep dive of some favorites. + +Umar Hansa has great materials for learning what the DevTools can do. He has [a weekly newsletter and GIFs](https://umaar.com/dev-tips/), a new course linked in the last section, and [an article on our site](https://css-tricks.com/six-tips-for-chrome-devtools/). + +One of my favorite recent ones is this [CSS Tracker Enhancement](https://umaar.com/dev-tips/126-css-tracker/), shown here with permission from Umar. This shows all of the unused CSS so that you can understand the performance impact. + +![](https://cdn.css-tricks.com/wp-content/uploads/2017/04/Screen-Shot-2017-04-10-at-10.20.11-AM.png) + +The CSS tracker shows color-coded rules for used and unused sets. + +#### # Misc Tools #### + +- [What input](https://ten1seven.github.io/what-input/) is a global utility for tracking the current input method (mouse, keyboard or touch), as well as the current intent- this can be really good for tracking down accessiblity leaks (hat tip to Marcy Sutton, accessibility expert for this tipoff) +- [Ghostlabapp](https://www.vanamco.com/ghostlab/) is a pretty snazzy tool if you’re doing responsive development or checking anything deployed across a ton of devices. It offers synchronized web development, testing, and inspection. +- [Eruda is an awesome tool](http://eruda.liriliri.io/) that helps debug on mobile devices. I really like it because it takes a simulator a step further, gives a console and real devtools to help you gain understanding. + +![eruda gives you a mobile console](https://cdn.css-tricks.com/wp-content/uploads/2017/04/Screen-Shot-2017-04-10-at-10.38.57-AM.png) + +### # Specific Tips ### + +I am always interested in what other people do to debug, so I asked the community through the CSS-Tricks account and my own what they were really into. This list is a mixture of tips I like as well as a roundup of tips from the community. + +#### # Accessibility #### + +``` +$('body').on('focusin',function(){ + console.log(document.activeElement);}); +``` + +> This logs the currently focused element, useful because opening the Devtools blurs the activeElement + +-[Marcy Sutton](https://twitter.com/marcysutton) + +#### # Debugging CSS #### + +We got quite a lot of responses saying that people put red borders on elements to see what they’re doing + +> [@sarah_edo](https://twitter.com/sarah_edo) for CSS, I'll usually have a .debug class with a red border that I slap on troublesome elements. +> +> — Jeremy Wagner (@malchata) [March 15, 2017](https://twitter.com/malchata/status/842029469246324736) + +I do this too, I even have a little CSS file that drops in some classes I can access for different colors easily. + +#### # Checking State in React #### + +> [@sarah_edo](https://twitter.com/sarah_edo)
{JSON.stringify(this.state, null, 2)}
+> +> — MICHAEL JACKSON (@mjackson) [March 15, 2017](https://twitter.com/mjackson/status/842041642760646657) + +Props to Michael, this is one of the most useful debugging tools I know of. That snippet "pretty prints" the state of the component you're working with onto the component so that you can see what’s going on. You can validate that the state is working the way that you think it should be, and it helps track down any errors between the state and how you're using it. + +#### # Animation #### + +We got a lot of responses that said they slow the animation way down: + +> [@sarah_edo](https://twitter.com/sarah_edo)[@Real_CSS_Tricks](https://twitter.com/Real_CSS_Tricks) * { animation-duration: 10s !important; } +> +> — Thomas Fuchs (@thomasfuchs) [March 15, 2017](https://twitter.com/thomasfuchs/status/842029720820695040) + +I mentioned this on a post I wrote right here on CSS Tricks about [debugging CSS Keyframe animations](https://css-tricks.com/debugging-css-keyframe-animations/), there are more tips too, like how to hardware accelerate, or work with multiple transforms in different percentages. + +I also slow down my animations in JavaScript- in GreenSock that would look like: `timeline.timeScale(0.5)` (you can slow down the whole timeline, not just one thing at a time, which is super useful), in mo.js that would look like `{speed: 0.5}`. + +[Val Head has a great screencast](https://www.youtube.com/watch?v=MjRipmP7ffM&feature=youtu.be) going through both chrome and firefox devtools offering on animation. + +If you want to use the Chrome Devtools timeline to do performance audits, it's worth mentioning that painting is the most expense of the tasks, so all things being equal, pay a little more attention to a high percentage of that green. + +#### # Checking different connection speeds and loads #### + +I tend to work on fast connections, so I will throttle my connection to check and see what the performance would look like for people who don’t have my internet speed. + +![throttle connection in devtools](https://cdn.css-tricks.com/wp-content/uploads/2017/04/Screen-Shot-2017-04-10-at-9.29.00-AM.png) + +This is also useful in conjunction with a hard reload, or with the cache empty + +> [@sarah_edo](https://twitter.com/sarah_edo) Not so secret trick. But still many people are unaware. You need DevTools open, and then right click over the refresh button. [pic.twitter.com/FdAfF9Xtxm](https://t.co/FdAfF9Xtxm) +> +> — David Corbacho (@dcorbacho) [March 15, 2017](https://twitter.com/dcorbacho/status/842033259664035840) + +#### # Set a Timed Debugger #### + +This one came from Chris. We have a whole writeup on it [right here](https://css-tricks.com/set-timed-debugger-web-inspect-hard-grab-elements/): + +``` +setTimeout(function() { + debugger; +}, 3000); +``` + +It’s similar to the debugger; tool I mentioned earlier, except you can put it in a setTimeout function and get even more fine-tuned information + +#### # Simulators #### + +> [@Real_CSS_Tricks](https://twitter.com/Real_CSS_Tricks) And just in case any Mac users didn't know this, iOS simulator + Safari is sweet. [pic.twitter.com/Uz4XO3e6uD](https://t.co/Uz4XO3e6uD) +> +> — Chris Coyier (@chriscoyier) [March 15, 2017](https://twitter.com/chriscoyier/status/842034009060302848) + +I mentioned simulators with Eruda before. iOS users also get a pretty sweet simulator. I was going to tell you you have to install XCode first, but this tweet showed another way: + +> [@chriscoyier](https://twitter.com/chriscoyier)[@Real_CSS_Tricks](https://twitter.com/Real_CSS_Tricks) Or, you can use this approach if you didn't want to bother with installing xCode: [https://t.co/WtAnZNo718](https://t.co/WtAnZNo718) +> +> — Chris Harrison (@cdharrison) [March 15, 2017](https://twitter.com/cdharrison/status/842038887904088065) + +Chrome also has a device toggle which is helpful. + +#### # Remote Debuggers #### + +> [@chriscoyier](https://twitter.com/chriscoyier)[@Real_CSS_Tricks](https://twitter.com/Real_CSS_Tricks)[https://t.co/q3OfWKNlUo](https://t.co/q3OfWKNlUo) is a good tool. +> +> — Gilles 💾⚽ (@gfra54) [March 15, 2017](https://twitter.com/gfra54/status/842035375304523777) + +I actually didn't know about this tool until seeing this tweet. Pretty useful! + +#### # CSS Grid Debugging #### (#article-header-id-18 .has-header-link) + +Rachel Andrew gave a presentation at Smashing and mentioned a little waffle thing you can click on in Firefox that will illuminate the gutters in the grid. [Her video](http://gridbyexample.com/learn/2016/12/17/learning-grid-day17/) explains it really eloquently. + +![](https://cdn.css-tricks.com/wp-content/uploads/2017/04/Screen-Shot-2017-04-10-at-9.58.14-AM.png) + +Rachel Andrew shows how to highlight gutters in Firefox DevTools. + +#### # Array Debugging #### + +Wes Bos with a really useful tip for searching for a single item in an array: + +> If you are just looking for a single item array.find() is 🔥 [https://t.co/AuRtyFwnq7](https://t.co/AuRtyFwnq7) +> +> — Wes Bos (@wesbos) [March 15, 2017](https://twitter.com/wesbos/status/842069915158884354) + +### # Further Debugging Resources ### + +Jon Kuperman has a [Frontend Masters course](https://frontendmasters.com/courses/chrome-dev-tools/) that can help you master devtools it goes along [with this app](https://github.com/jkup/mastering-chrome-devtools). + +There’s a small course on code school called [discover devtools](https://www.codeschool.com/courses/discover-devtools). + +Umar Hansa has a new online course called [Modern DevTools](https://moderndevtools.com/). + +Julia Evans has a great article [about debugging here](http://jvns.ca/blog/2015/11/22/how-i-got-better-at-debugging/), hat tip to Jamison Dance for showing it to me. + +Paul Irish does some [advanced performance audits with devtools](https://docs.google.com/document/d/1K-mKOqiUiSjgZTEscBLjtjd6E67oiK8H2ztOiq5tigk/pub) if you're super nerdy like me and want to dig into the timeline. + +Finally, I'll put in a bittersweet resource. My friend James Golick who was an excellent programmer and even more excellent human gave this great conference talk about debugging anything many years ago. Sadly James has passed, but we can still honor his memory and learn from him: + +--- + +> [掘金翻译计划](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)、[React](https://github.com/xitu/gold-miner#react)、[前端](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)。 From 863a5bc2e3f1f08165f87df9039e310393325ee6 Mon Sep 17 00:00:00 2001 From: sqrthree Date: Thu, 13 Apr 2017 23:07:26 +0800 Subject: [PATCH 158/638] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20=E7=BB=86=E8=8A=82?= =?UTF-8?q?=E6=98=AF=E4=BA=A7=E5=93=81=E8=AE=BE=E8=AE=A1=E7=9A=84=E9=87=8D?= =?UTF-8?q?=E4=B8=AD=E4=B9=8B=E9=87=8D=20=E7=9A=84=E7=A7=AF=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- product.md | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0179de489e5..982f0ed40da 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,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) 上的英文分享文章。内容覆盖 [Android](#android)、[iOS](#ios)、[React](#react)、[前端](#前端)、[后端](#后端)、[产品](#产品)、[设计](#设计) 等领域,读者为热爱新技术的新锐开发者。 -掘金翻译计划目前翻译完成 [455](#近期文章列表) 篇文章,共有 [300](https://github.com/xitu/gold-miner/wiki/%E8%AF%91%E8%80%85%E7%A7%AF%E5%88%86%E8%A1%A8) 余名译者贡献翻译。 +掘金翻译计划目前翻译完成 [456](#近期文章列表) 篇文章,共有 [300](https://github.com/xitu/gold-miner/wiki/%E8%AF%91%E8%80%85%E7%A7%AF%E5%88%86%E8%A1%A8) 余名译者贡献翻译。 # 官方指南: @@ -81,8 +81,8 @@ ## 产品 +* [细节是产品设计的重中之重](https://juejin.im/post/58ed96aaa22b9d00634732e9/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([iloveivyxuan](https://github.com/iloveivyxuan) 翻译) * [单元测试,精益创业,以及两者之间的关系](https://juejin.im/post/58d90a3b44d90400694505c4/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([gy134340](http://gy134340.com/) 翻译) * [你正在阅读的用户体验文章是不是在向你进行推销?](https://juejin.im/post/58d4c501a22b9d00645544d9/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([ylq167](https://github.com/ylq167) 翻译) * [直观设计 vs. 共享式设计](https://gold.xitu.io/entry/5862650a128fe1006d04d398/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([Funtrip](https://www.behance.net/Funtrip) 翻译) -* [为何而设计?](https://gold.xitu.io/entry/5857969761ff4b00686ad66b/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([王子建](https://github.com/Romeo0906) 翻译) * [所有产品译文>>](https://github.com/xitu/gold-miner/blob/master/product.md) diff --git a/product.md b/product.md index 308a8319f6f..be018fddc3b 100644 --- a/product.md +++ b/product.md @@ -1,3 +1,4 @@ +* [细节是产品设计的重中之重](https://juejin.im/post/58ed96aaa22b9d00634732e9/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([iloveivyxuan](https://github.com/iloveivyxuan) 翻译) * [单元测试,精益创业,以及两者之间的关系](https://juejin.im/post/58d90a3b44d90400694505c4/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([gy134340](http://gy134340.com/) 翻译) * [你正在阅读的用户体验文章是不是在向你进行推销?](https://juejin.im/post/58d4c501a22b9d00645544d9/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([ylq167](https://github.com/ylq167) 翻译) * [直观设计 vs. 共享式设计](https://gold.xitu.io/entry/5862650a128fe1006d04d398/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([Funtrip](https://www.behance.net/Funtrip) 翻译) From 2cfde921be0d1ede43141d9b345f32aed73f45ed Mon Sep 17 00:00:00 2001 From: sqrthree Date: Thu, 13 Apr 2017 23:13:13 +0800 Subject: [PATCH 159/638] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20=E5=90=8C=E4=B8=AD?= =?UTF-8?q?=E6=9C=89=E5=BC=82=E7=9A=84=20Webpack=20=E4=B8=8E=20Rollup=20?= =?UTF-8?q?=E7=9A=84=E7=A7=AF=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- front-end.md | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 982f0ed40da..8db95c9f37f 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,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) 上的英文分享文章。内容覆盖 [Android](#android)、[iOS](#ios)、[React](#react)、[前端](#前端)、[后端](#后端)、[产品](#产品)、[设计](#设计) 等领域,读者为热爱新技术的新锐开发者。 -掘金翻译计划目前翻译完成 [456](#近期文章列表) 篇文章,共有 [300](https://github.com/xitu/gold-miner/wiki/%E8%AF%91%E8%80%85%E7%A7%AF%E5%88%86%E8%A1%A8) 余名译者贡献翻译。 +掘金翻译计划目前翻译完成 [457](#近期文章列表) 篇文章,共有 [300](https://github.com/xitu/gold-miner/wiki/%E8%AF%91%E8%80%85%E7%A7%AF%E5%88%86%E8%A1%A8) 余名译者贡献翻译。 # 官方指南: @@ -37,10 +37,10 @@ ## 前端 +* [同中有异的 Webpack 与 Rollup](https://juejin.im/post/58edb865570c350057f199a7?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([lsvih](https://github.com/lsvih) 翻译) * [webpack 拾翠:充分利用 CommonsChunkPlugin()](https://juejin.im/post/58ec4e3f5c497d0062c470bf?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([reid3290](https://github.com/reid3290) 翻译) * [写给“老派” Web 开发者的“现代” JavaScript 指南](https://juejin.im/post/58ebab0c8d6d81006191376f?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([SunCafe](http://suncafe.cc/) 翻译) * [CSS很棒,只是真的太难了](https://juejin.im/entry/58eae24a61ff4b0061a6a102?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([ZhangFe](https://github.com/ZhangFe) 翻译) -* [Preload,Prefetch 和它们在 Chrome 之中的优先级](https://juejin.im/post/58e8acf10ce46300585a7a42?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([gy134340](http://gy134340.com/) 翻译) * [所有前端译文>>](https://github.com/xitu/gold-miner/blob/master/front-end.md) diff --git a/front-end.md b/front-end.md index 1d501a043c1..e646fb5aeff 100644 --- a/front-end.md +++ b/front-end.md @@ -1,3 +1,4 @@ +* [同中有异的 Webpack 与 Rollup](https://juejin.im/post/58edb865570c350057f199a7?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([lsvih](https://github.com/lsvih) 翻译) * [webpack 拾翠:充分利用 CommonsChunkPlugin()](https://juejin.im/post/58ec4e3f5c497d0062c470bf?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([reid3290](https://github.com/reid3290) 翻译) * [写给“老派” Web 开发者的“现代” JavaScript 指南](https://juejin.im/post/58ebab0c8d6d81006191376f?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([SunCafe](http://suncafe.cc/) 翻译) * [CSS很棒,只是真的太难了](https://juejin.im/entry/58eae24a61ff4b0061a6a102?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([ZhangFe](https://github.com/ZhangFe) 翻译) From 95989982386812d5e814660828cd3a7c0f224f25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=B9=E5=8F=B7=E4=B8=89?= Date: Thu, 13 Apr 2017 23:13:54 +0800 Subject: [PATCH 160/638] Update function-as-child-components.md --- TODO/function-as-child-components.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO/function-as-child-components.md b/TODO/function-as-child-components.md index 761cef8ecbd..8a7ee9897c7 100644 --- a/TODO/function-as-child-components.md +++ b/TODO/function-as-child-components.md @@ -187,7 +187,7 @@ Ratio.defaultProps = { Alright, so I did a lot there. We added some event listeners to listen for resize events as well as actually computing the width and height using the provided ratio. Neat, so we’ve got a width and height in our internal state, how can we share it with other components? -This is one of those things that is hard to understand because it is so simple that when you see it you think, “That can’t be all there is to it.” but this ***is***all there is to it. +This is one of those things that is hard to understand because it is so simple that when you see it you think, “That can’t be all there is to it.” but this is all there is to it. #### Children is literally just a JavaScript function. #### From f1201ddf9ef897e77cd6537a7deab9e62d135a42 Mon Sep 17 00:00:00 2001 From: sqrthree Date: Thu, 13 Apr 2017 23:20:52 +0800 Subject: [PATCH 161/638] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20MVVM-C=20=E4=B8=8E?= =?UTF-8?q?=20Swift=20=E7=9A=84=E7=A7=AF=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- ios.md | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8db95c9f37f..b8e4a9f3caa 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,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) 上的英文分享文章。内容覆盖 [Android](#android)、[iOS](#ios)、[React](#react)、[前端](#前端)、[后端](#后端)、[产品](#产品)、[设计](#设计) 等领域,读者为热爱新技术的新锐开发者。 -掘金翻译计划目前翻译完成 [457](#近期文章列表) 篇文章,共有 [300](https://github.com/xitu/gold-miner/wiki/%E8%AF%91%E8%80%85%E7%A7%AF%E5%88%86%E8%A1%A8) 余名译者贡献翻译。 +掘金翻译计划目前翻译完成 [458](#近期文章列表) 篇文章,共有 [300](https://github.com/xitu/gold-miner/wiki/%E8%AF%91%E8%80%85%E7%A7%AF%E5%88%86%E8%A1%A8) 余名译者贡献翻译。 # 官方指南: @@ -29,10 +29,10 @@ ## iOS +* [MVVM-C 与 Swift](https://juejin.im/post/58ef16b8da2f60005d180666/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([DeepMissea](https://github.com/DeepMissea)) 翻译) * [Swift 闭包和代理中的保留周期](https://juejin.im/post/58e4ac5d44d904006d2a9a19/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([oOatuo](http://atuo.xyz/)) 翻译) * [一名 iOS 开发者的 React Native 开发经历](https://juejin.im/post/58df4c3fa0bb9f0069e2f2bd/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([lsvih](https://github.com/lsvih)) 翻译) * [优化 Swift 的编译时间](https://juejin.im/post/58df1d75570c35005796966b/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([DeepMissea](https://github.com/DeepMissea)) 翻译) -* [看!Swift 里竟然有红绿灯 🚦!](https://juejin.im/post/58ddbc612f301e0062fed742/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([DeepMissea](https://github.com/DeepMissea)) 翻译) * [所有 iOS 译文>>](https://github.com/xitu/gold-miner/blob/master/ios.md) ## 前端 diff --git a/ios.md b/ios.md index 53fce8c3fce..89d29887e73 100644 --- a/ios.md +++ b/ios.md @@ -1,3 +1,4 @@ +* [MVVM-C 与 Swift](https://juejin.im/post/58ef16b8da2f60005d180666/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([DeepMissea](https://github.com/DeepMissea)) 翻译) * [Swift 闭包和代理中的保留周期](https://juejin.im/post/58e4ac5d44d904006d2a9a19/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([oOatuo](http://atuo.xyz/)) 翻译) * [一名 iOS 开发者的 React Native 开发经历](https://juejin.im/post/58df4c3fa0bb9f0069e2f2bd/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([lsvih](https://github.com/lsvih)) 翻译) * [优化 Swift 的编译时间](https://juejin.im/post/58df1d75570c35005796966b/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([DeepMissea](https://github.com/DeepMissea)) 翻译) From 548991b587ae880f4e24df0ce4c8720472e39781 Mon Sep 17 00:00:00 2001 From: gy134340 Date: Thu, 13 Apr 2017 23:25:29 +0800 Subject: [PATCH 162/638] =?UTF-8?q?=E5=AE=A1=E6=A0=B8=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...amming-in-javascript-composing-software.md | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/TODO/why-learn-functional-programming-in-javascript-composing-software.md b/TODO/why-learn-functional-programming-in-javascript-composing-software.md index c260bf55297..76ee8309ca0 100644 --- a/TODO/why-learn-functional-programming-in-javascript-composing-software.md +++ b/TODO/why-learn-functional-programming-in-javascript-composing-software.md @@ -1,8 +1,8 @@ > * 原文地址:[Why Learn Functional Programming in JavaScript? (Composing Software)(part 2)](https://medium.com/javascript-scene/why-learn-functional-programming-in-javascript-composing-software-ea13afc7a257) > * 原文作者:[Eric Elliott](https://medium.com/@_ericelliott?source=post_header_lockup) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 译者: -> * 校对者: +> * 译者:[gy134340](https://github.com/gy134340) +> * 校对者:[sunui](https://github.com/sunui),[avocadowang](https://github.com/avocadowang) # 为什么用 JavaScript 学习函数式编程?(软件构建)(第二部分) @@ -10,7 +10,7 @@ 烟雾的方块艺术 —MattysFlicks —(CC BY 2.0) > 注意:这是从基础学习函数式编程和使用 JavaScript ES6+ 撰写软件的第二部分。保持关注,接下来还有很多! -> [从第一部分开始](https://medium.com/javascript-scene/the-rise-and-fall-and-rise-of-functional-programming-composable-software-c2d91b424c8c#.2dfd6n6qe) | [接下来的 >](https://medium.com/javascript-scene/a-functional-programmers-introduction-to-javascript-composing-software-d670d14ede30#.2e4youss2) +> [从第一部分开始](https://github.com/xitu/gold-miner/blob/master/TODO/the-rise-and-fall-and-rise-of-functional-programming-composable-software.md) | [接下来的 >](https://github.com/xitu/gold-miner/blob/master/TODO/a-functional-programmers-introduction-to-javascript-composing-software.md) 忘掉你认为知道的关于 JavaScript 的一切,用初学者的眼光去看待它。为了帮助你做到这一点,我们将会从头复习一下 JavaScript 的基础,就像你与其尚未谋面一样。如果你是初学者,那你就很幸运了。最终从零开始探索 ES6 和函数式编程!希望所有的概念都被解释清楚 — 但不要太依赖于此。 @@ -20,9 +20,9 @@ JavaScript 有函数式编程所需要的最重要的特性: -1. **一级公民函数:**使用函数作为数据值的能力:用函数传参,返回函数,用函数做变量和对象属性。这个属性允许更高级别的函数,使偏函数应用、柯里化和组合成为可能。 -2. **匿名函数和简洁的 lambda 语法:**`x => x * 2` 是 JavaScript 中有效的函数表达式。简洁的 lambda 语法使得高阶函数变的简单。 -3. **闭包:**闭包是一个有着自己独立作用域的捆绑函数。闭包在函数被创建时被创建。当一个函数在另一个函数内部被创建,它可以访问外部函数的变量,即使在外部函数退出后。通过闭包偏函数应用可以获取内部固定参数。固定的参数时绑定在返回函数的作用域范围内的参数。在 `add2(1)(2)` 中,`1` 是 `add2(1)` 返回的函数中的固定参数。 +1. **一级公民函数:** 使用函数作为数据值的能力:用函数传参,返回函数,用函数做变量和对象属性。这个属性允许更高级别的函数,使偏函数应用、柯里化和组合成为可能。 +2. **匿名函数和简洁的 lambda 语法:** `x => x * 2` 是 JavaScript 中有效的函数表达式。简洁的 lambda 语法使得高阶函数变的简单。 +3. **闭包:** 闭包是一个有着自己独立作用域的捆绑函数。闭包在函数被创建时被创建。当一个函数在另一个函数内部被创建,它可以访问外部函数的变量,即使在外部函数退出后。通过闭包偏函数应用可以获取内部固定参数。固定的参数时绑定在返回函数的作用域范围内的参数。在 `add2(1)(2)` 中,`1` 是 `add2(1)` 返回的函数中的固定参数。 ### JavaScript 缺少了什么 @@ -40,19 +40,19 @@ JavaScript 是多范式语言,意味着它支持多种风格的编程。其他 下面是一些函数式语言拥有但是 JavaScript 没有的特性: -1. **纯粹性:**在一些函数式语言中,纯粹性是强制的,有副作用的表达式是不被允许的。 -2. **不可变性:**一些函数式语言不允许转变,采用表达式来产生新的数据结构来代替更改一个已存的数据结构,比如说数组或者对象。这样看起来可能不够高效,但是大多数函数式语言在引擎下使用 trie 数据结构,具有结构共享的特点:意味着旧的对象和新的对象是对相同数据的引用。 -3. **递归:**递归是函数引用自身来进行迭代的能力。在大多数函数式语言中,递归是迭代的唯一方式,它们没有像 `for` 、`while`、`do` 这类循环语句。 +1. **纯粹性:** 在一些函数式语言中,纯粹性是强制的,有副作用的表达式是不被允许的。 +2. **不可变性:** 一些函数式语言不允许转变,采用表达式来产生新的数据结构来代替更改一个已存的数据结构,比如说数组或者对象。这样看起来可能不够高效,但是大多数函数式语言在引擎下使用 trie 数据结构,具有结构共享的特点:意味着旧的对象和新的对象是对相同数据的引用。 +3. **递归:** 递归是函数引用自身来进行迭代的能力。在大多数函数式语言中,递归是迭代的唯一方式,它们没有像 `for` 、`while`、`do` 这类循环语句。 -**纯粹性:**在 JavaScript 中,纯粹性由约定来达成,如果你不是使用纯函数来构成你的大多数应用,那么你就不是在进行函数式风格的编程。很不幸,在 JavaScript 中,你很容易就会不小心创建和使用一些不纯的函数。 +**纯粹性:** 在 JavaScript 中,纯粹性由约定来达成,如果你不是使用纯函数来构成你的大多数应用,那么你就不是在进行函数式风格的编程。很不幸,在 JavaScript 中,你很容易就会不小心创建和使用一些不纯的函数。 -**不可变性:**在纯函数式语言中,不可变性通常是强制的,JavaScript 缺少函数式语言中高效的、基于 trie 树的数据结构,但是你可以使用一些库,包括 [Immutable.js](https://facebook.github.io/immutable-js/) 和 [Mori](https://github.com/swannodette/mori),由衷期望未来的 ECMAScript 规范版本可以拥抱不可变数据结构。 +**不可变性:** 在纯函数式语言中,不可变性通常是强制的,JavaScript 缺少函数式语言中高效的、基于 trie 树的数据结构,但是你可以使用一些库,包括 [Immutable.js](https://facebook.github.io/immutable-js/) 和 [Mori](https://github.com/swannodette/mori),由衷期望未来的 ECMAScript 规范版本可以拥抱不可变数据结构。 有一些迹象带来了希望,比如说在 ES6 中添加了 `const` 关键字,`const` 声明的变量不能被重新赋值,重要的是要理解 `const` 所声明的值并不是不可改变的。 `const` 声明的对象不能被重新声明为新的对象,但是对象的属性却是可变的,JavaScript 有 `freeze()` 对象的能力,但是这些对象只能在根实例上被冻结,意味着嵌套着的对象还是可以改变它的属性。换句话说,在 JavaScript 规范中看到真正的不可变还有很长的路要走。 -**递归:**JavaScript 技术上支持递归,但是大多数函数式语言都有尾部调用优化的特性,尾部调用优化是一个允许递归的函数重用堆栈帧来递归调用的特性。 +**递归:** JavaScript 技术上支持递归,但是大多数函数式语言都有尾部调用优化的特性,尾部调用优化是一个允许递归的函数重用堆栈帧来递归调用的特性。 没有尾部调用优化,一个调用的栈很可能没有边界导致堆栈溢出。JavaScript 在 ES6 规范中有一个有限的尾调用优化。不幸的是,只有一个主要的浏览器引擎支持它,这个优化被部分应用随后从 Babel(最流行的 JavaScript 编译器,在旧的浏览器中被用来把 ES6 编译到 ES5) 中移除。 @@ -64,7 +64,7 @@ JavaScript 是多范式语言,意味着它支持多种风格的编程。其他 Monads 的问题是,尽管它的使用很简单,但是对一个不是很熟悉它的人解释清楚它有点像“对牛谈琴”。 -> “Monad说白了不过就是自函子范畴上的一个幺半群而已,这有什么难以理解的?” ~James Iry 所引用 Philip Wadler 的话,解释一个 Saunders Mac Lane 说过的名言。[*“编程语言简要、不完整之黑历史”*](http://james-iry.blogspot.com/2009/05/brief-incomplete-and-mostly-wrong.html) +> “Monad说白了不过就是自函子范畴上的一个幺半群而已,这有什么难以理解的?” ~James Iry 所引用 Philip Wadler 的话,解释一个 Saunders Mac Lane 说过的名言。[**“编程语言简要、不完整之黑历史”**](http://james-iry.blogspot.com/2009/05/brief-incomplete-and-mostly-wrong.html) 典型的,这是在调侃这有趣的一点。在上面的引用中,关于 Monads 的解释相比最初的有了很大的简化,原来是下面这样: @@ -135,7 +135,7 @@ JavaScript 的真正优势在于其生态系统中的思想和用户的多样性 App 正在吞食世界, web 正在吞食 app, 同时 JavaScript 正在吞食 web。 -[**接下来的第三部分: 函数式开发者的 JavScript 介绍…**](https://medium.com/javascript-scene/a-functional-programmers-introduction-to-javascript-composing-software-d670d14ede30#.zdpw16p65) +[**接下来的第三部分: 函数式开发者的 JavScript 介绍…**](https://github.com/xitu/gold-miner/blob/master/TODO/a-functional-programmers-introduction-to-javascript-composing-software.md) ### 下一步 @@ -148,7 +148,7 @@ App 正在吞食世界, web 正在吞食 app, 同时 JavaScript 正在吞食 *Eric Elliott* 是 [*“Programming JavaScript Applications”*](http://pjabook.com) (O’Reilly) 和 “Learn JavaScript with Eric Elliott” 的作者。他曾效力于 *Adobe Systems, Zumba Fitness, he Wall Street Journal, ESPN, BBC, and top recording artists including Usher, Frank Ocean, Metallica* 和其他一些公司。 -*他和她的老婆(很漂亮)大部分时间都在旧金山湾区里。* +**他和她的老婆(很漂亮)大部分时间都在旧金山湾区里。** --- From faead526a8bbdb96a5e142a35150f99f78493622 Mon Sep 17 00:00:00 2001 From: gy134340 Date: Thu, 13 Apr 2017 23:29:35 +0800 Subject: [PATCH 163/638] Change name --- ...n-functional-programming-in-javascript-composing-software.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO/why-learn-functional-programming-in-javascript-composing-software.md b/TODO/why-learn-functional-programming-in-javascript-composing-software.md index 76ee8309ca0..1a9327f182e 100644 --- a/TODO/why-learn-functional-programming-in-javascript-composing-software.md +++ b/TODO/why-learn-functional-programming-in-javascript-composing-software.md @@ -4,7 +4,7 @@ > * 译者:[gy134340](https://github.com/gy134340) > * 校对者:[sunui](https://github.com/sunui),[avocadowang](https://github.com/avocadowang) -# 为什么用 JavaScript 学习函数式编程?(软件构建)(第二部分) +# 为什么用 JavaScript 学习函数式编程?(软件编写)(第二部分) From 5b1b51ebdafc4dff22796cad47f941d7edb82f82 Mon Sep 17 00:00:00 2001 From: gy134340 Date: Thu, 13 Apr 2017 23:31:51 +0800 Subject: [PATCH 164/638] Change title --- ...-and-rise-of-functional-programming-composable-software.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TODO/the-rise-and-fall-and-rise-of-functional-programming-composable-software.md b/TODO/the-rise-and-fall-and-rise-of-functional-programming-composable-software.md index a529896a3c1..c02cffb517d 100644 --- a/TODO/the-rise-and-fall-and-rise-of-functional-programming-composable-software.md +++ b/TODO/the-rise-and-fall-and-rise-of-functional-programming-composable-software.md @@ -4,13 +4,13 @@ > * 译者:[gy134340](https://github.com/gy134340) > * 校对者:[avocadowang](https://github.com/avocadowang),[Aladdin-ADD](https://github.com/Aladdin-ADD) -# 跌宕起伏的函数式编程(软件构建) +# 跌宕起伏的函数式编程(软件编写) 烟雾的方块艺术 —MattysFlicks —(CC BY 2.0) -> 注意:这是从基础学习函数式编程和使用 JavaScript ES6+ 构建软件的第一部分。保持关注,接下来还有很多! +> 注意:这是从基础学习函数式编程和使用 JavaScript ES6+ 编写软件的第一部分。保持关注,接下来还有很多! 当我 6 岁时,我花了很多时间跟我的小伙伴玩电脑游戏,他家有一个装满电脑的房间。对于我说,它们有不可抗拒的魔力。我花了很多时间探索所有的游戏。一天我问他,“我们怎样做一个游戏?” From e4b0de65a66405a4f0afb4b5c5f897f8ec8efedd Mon Sep 17 00:00:00 2001 From: zhuzi Date: Fri, 14 Apr 2017 01:05:56 +0800 Subject: [PATCH 165/638] update translation --- TODO/secure-web-app-http-headers.md | 56 ++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/TODO/secure-web-app-http-headers.md b/TODO/secure-web-app-http-headers.md index af59d9b41f1..d15fe288045 100644 --- a/TODO/secure-web-app-http-headers.md +++ b/TODO/secure-web-app-http-headers.md @@ -219,13 +219,17 @@ functionrequestHandler(req, res){ res.setHeader('X-XSS-Protection','1;mode=block');} ``` -### Controlling Framing ### +### Controlling Framing 控制 iframe ### -An iframe (or HTML inline frame element, if you want to be more formal) is a DOM element that allows a web app to be nested within a parent web app. This powerful element enables some important web use cases, such as embedding third-party content into web apps, but it also has significant drawbacks, such as not being SEO-friendly and not playing nice with browser navigation — the list goes on. +> An iframe (or HTML inline frame element, if you want to be more formal) is a DOM element that allows a web app to be nested within a parent web app. This powerful element enables some important web use cases, such as embedding third-party content into web apps, but it also has significant drawbacks, such as not being SEO-friendly and not playing nice with browser navigation — the list goes on. -One of the caveats of iframes is that it makes clickjacking easier. Clickjacking is an attack that tricks the user into clicking something different than what they think they’re clicking. To understand a simple implementation of clickjacking, consider the HTML markup below, which tries to trick the user into buying a toaster when they think they are clicking to win a prize! +iframe (正式来说,是一个 HTML 的行内框架元素)是一个 DOM 元素,它允许一个 web 应用嵌套在另一个 web 应用中。这个强大的元素有部分重要的使用场景,比如在 web 应用中嵌入第三方内容, 但它也有重大的缺点,例如对 SEO 不友好,对浏览器导航跳转也不友好,还有很多。 -``` +> One of the caveats of iframes is that it makes clickjacking easier. Clickjacking is an attack that tricks the user into clicking something different than what they think they’re clicking. To understand a simple implementation of clickjacking, consider the HTML markup below, which tries to trick the user into buying a toaster when they think they are clicking to win a prize! + +其中一个需要注意的事是它是的点击劫持变得更加容易。点击劫持是一种让用户点击与他们想要点击的不同的攻击。要理解一个简单的劫持实现,参考以下 HTML,当用户认为他们点击可以获得奖品时,实际上是试图欺骗用户购买面包机。 + +```html @@ -234,38 +238,56 @@ One of the caveats of iframes is that it makes clickjacking easier. Clickjacking ``` -Clickjacking has many malicious applications, such as tricking the user into confirming a Facebook like, purchasing an item online and even submitting confidential information. Malicious web apps can leverage iframes for clickjacking by embedding a legitimate web app inside their malicious web app, rendering the iframe invisible with the `opacity: 0` CSS rule, and placing the iframe’s click target directly on top of an innocent-looking button rendered by the malicious web app. A user who clicks the innocent-looking button will trigger a click on the embedded web app — without at all knowing the effect of their click. +> Clickjacking has many malicious applications, such as tricking the user into confirming a Facebook like, purchasing an item online and even submitting confidential information. Malicious web apps can leverage iframes for clickjacking by embedding a legitimate web app inside their malicious web app, rendering the iframe invisible with the `opacity: 0` CSS rule, and placing the iframe’s click target directly on top of an innocent-looking button rendered by the malicious web app. A user who clicks the innocent-looking button will trigger a click on the embedded web app — without at all knowing the effect of their click. + +点击劫持有许多恶意应用程序,例如欺骗用户确认 Facebook 点赞,在线购买商品,甚至提交机密信息。恶意 web 应用程序可以通过在其恶意应用中嵌入合法的 web 应用来利用 iframe 进行点击劫持,这可以通过设置 `opacity: 0` 的 CSS 规则将其隐藏,并将 iframe 的点击目标直接放置在看起来无辜的按钮之上。点击了这个无辜按钮的用户会直接点击在嵌入的 web 应用上,并不知道点击后的作用。 -An effective way to block this attack is by restricting your web app from being framed. `X-Frame-Options`, specified in [RFC 7034](https://www.ietf.org/rfc/rfc7034.txt), is designed to do exactly that! This header instructs the browser to apply limitations on whether your web app can be embedded within another web page, thus blocking a malicious web page from tricking users into invoking various transactions on your web app. You can either block framing completely using the `DENY` directive, whitelist specific domains using the `ALLOW-FROM` directive, or whitelist only the web app’s origin using the `SAMEORIGIN` directive. +> An effective way to block this attack is by restricting your web app from being framed. `X-Frame-Options`, specified in [RFC 7034](https://www.ietf.org/rfc/rfc7034.txt), is designed to do exactly that! This header instructs the browser to apply limitations on whether your web app can be embedded within another web page, thus blocking a malicious web page from tricking users into invoking various transactions on your web app. You can either block framing completely using the `DENY` directive, whitelist specific domains using the `ALLOW-FROM` directive, or whitelist only the web app’s origin using the `SAMEORIGIN` directive. -My recommendation is to use the `SAMEORIGIN` directive, which enables iframes to be leveraged for apps on the same domain — which may be useful at times — and which maintains security. This recommended header looks like this: +阻止这种攻击的一种有效的方法是限制你的 web 应用被框架化。`X-Frame-Options`,在 [RFC 7034](https://www.ietf.org/rfc/rfc7034.txt) 中引入,就是设计用来做这件事的。此响应头指示浏览器对你的 web 应用是否可以被嵌入另一个网页进行限制,从而阻止恶意网页欺骗用户调用你的应用程序上的各种事务。你可以使用 `DENY` 完全屏蔽,或者使用 `ALLOW-FROM` 指定将特定域列入白名单,也可以使用 `SAMEORIGIN` 指令将应用的源地址列入白名单。 + +> My recommendation is to use the `SAMEORIGIN` directive, which enables iframes to be leveraged for apps on the same domain — which may be useful at times — and which maintains security. This recommended header looks like this: + +我的建议是使用 `SAMEORIGIN` 指令,因为它允许 iframe 被用于同一域上的可以保证安全性的应用程序,这有时是有用的。 ``` X-Frame-Options: SAMEORIGIN ``` -Here’s an example of a configuration of this header to enable framing on the same origin in Node.js: +以下是在 Node.js 中设置此响应头的示例代码: -``` +```javascript functionrequestHandler(req, res){ res.setHeader('X-Frame-Options','SAMEORIGIN');} ``` -### Explicitly Whitelisting Sources ### +### 指定白名单资源 ### -As we’ve noted earlier, you can add in-depth security to your web app by enabling the browser’s XSS filter. However, note that this mechanism is limited, is not supported by all browsers (Firefox, for instance, does not have an XSS filter) and relies on pattern-matching techniques that can be tricked. +> As we’ve noted earlier, you can add in-depth security to your web app by enabling the browser’s XSS filter. However, note that this mechanism is limited, is not supported by all browsers (Firefox, for instance, does not have an XSS filter) and relies on pattern-matching techniques that can be tricked. -Another layer of in-depth protection against XSS and other attacks can be achieved by explicitly whitelisting trusted sources and operations — which is what Content Security Policy (CSP) enables web app developers to do. +如前所述,你可以通过启用浏览器的 XSS 过滤器,给你的 web 应用程序增强安全性。然而请注意,这种机制是有局限性的,不是所有浏览器都支持(例如 Firefox 就不支持 XSS 过滤),并且依赖的模式匹配技术可以被欺骗。 -CSP is a [W3C specification](https://www.w3.org/TR/2016/WD-CSP3-20160901/) that defines a powerful browser-based security mechanism, enabling granular control over resource-loading and script execution in a web app. With CSP, you can whitelist specific domains for operations such as script-loading, AJAX calls, image-loading and style sheet-loading. You can enable or disable inline scripts or dynamic scripts (the notorious `eval`) and control framing by whitelisting specific domains for framing. Another cool feature of CSP is that it allows you to configure a real-time reporting target, so that you can monitor your app in real time for CSP blocking operations. +> Another layer of in-depth protection against XSS and other attacks can be achieved by explicitly whitelisting trusted sources and operations — which is what Content Security Policy (CSP) enables web app developers to do. -This explicit whitelisting of resource loading and execution provides in-depth security that in many cases will fend off attacks. For example, by using CSP to disallow inline scripts, you can fend off many of the reflective XSS attack variants that rely on injecting inline scripts into the DOM. +对抗 XSS 和其他攻击的更多一层的保护,可以通过明确列出可信来源和操作来实现 —— 这就是内容安全策略(CSP)。 -CSP is a relatively complex header, with a lot of directives, and I won’t go into the details of the various directives. HTML5 Rocks has a [great tutorial](https://www.html5rocks.com/en/tutorials/security/content-security-policy/) that provides an overview of CSP, and I highly recommend reading it and learning how to use CSP in your web app. +> CSP is a [W3C specification](https://www.w3.org/TR/2016/WD-CSP3-20160901/) that defines a powerful browser-based security mechanism, enabling granular control over resource-loading and script execution in a web app. With CSP, you can whitelist specific domains for operations such as script-loading, AJAX calls, image-loading and style sheet-loading. You can enable or disable inline scripts or dynamic scripts (the notorious `eval`) and control framing by whitelisting specific domains for framing. Another cool feature of CSP is that it allows you to configure a real-time reporting target, so that you can monitor your app in real time for CSP blocking operations. -Here’s a simple example of a CSP configuration to allow script-loading from the app’s origin only and to block dynamic script execution (`eval`) and inline scripts (as usual, on Node.js): +CSP 是一种 W3C 规范,它定义了强大的基于浏览器的安全机制,可以对 web 应用中的资源加载以及脚本执行进行精细的控制。使用 CSP 可以将特定的域加入白名单进行例如脚本加载、AJAX 调用、图像加载和样式加载。你可以启用或禁用内嵌脚本或动态脚本(臭名昭著的 `eval`),并通过将特定域列入白名单来控制 iframe。CSP 的另一个很酷的功能是它允许配置实时报告目标,一遍实时监控应用程序进行 CSP 阻止操作。 -``` +> This explicit whitelisting of resource loading and execution provides in-depth security that in many cases will fend off attacks. For example, by using CSP to disallow inline scripts, you can fend off many of the reflective XSS attack variants that rely on injecting inline scripts into the DOM. + +这种对资源加载和脚本执行的明确的白名单提供了很强的安全性,在很多情况下都可以防范攻击。例如,使用 CSP 禁止内嵌脚本,你可以防范很多反射型 XSS 攻击,因为它们依赖于将内嵌脚本注入到 DOM。 + +> CSP is a relatively complex header, with a lot of directives, and I won’t go into the details of the various directives. HTML5 Rocks has a [great tutorial](https://www.html5rocks.com/en/tutorials/security/content-security-policy/) that provides an overview of CSP, and I highly recommend reading it and learning how to use CSP in your web app. + +CSP 是一个相对复杂的响应头,它有很多种指令,在这里我不详细展开了,可以参考 HTML5 Rocks 里一篇很棒的[教程](https://www.html5rocks.com/en/tutorials/security/content-security-policy/),提供了 CSP 的概述,我非常推荐阅读它来学习如何在你的 web 应用中使用 CSP。 + +> Here’s a simple example of a CSP configuration to allow script-loading from the app’s origin only and to block dynamic script execution (`eval`) and inline scripts (as usual, on Node.js): + +以下是一个设置 CSP 的示例代码,它仅允许从应用程序的源域加载脚本,并组织动态脚本的执行(eval)以及内嵌脚本(当然,还是 Node.js): + +```javascript functionrequestHandler(req, res){ res.setHeader('Content-Security-Policy',"script-src 'self'");} ``` From 1d57ea5b8c23304f3943e32de02ca3710c1d0ec7 Mon Sep 17 00:00:00 2001 From: Wenlin Ou Date: Thu, 13 Apr 2017 18:15:39 -0400 Subject: [PATCH 166/638] added proofreader info and error fixes --- TODO/nothing-will-change-until-you-start-building.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TODO/nothing-will-change-until-you-start-building.md b/TODO/nothing-will-change-until-you-start-building.md index f063274822b..f5c5b91bc89 100644 --- a/TODO/nothing-will-change-until-you-start-building.md +++ b/TODO/nothing-will-change-until-you-start-building.md @@ -2,7 +2,7 @@ > * 原文作者:[Jonathan Z. White](https://medium.freecodecamp.com/@JonathanZWhite?source=post_header_lockup) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 译者:[owenlyn](https://github.com/owenlyn) -> * 校对者: +> * 校对者:[luoqiuyu](https://github.com/luoqiuyu),[Tina92](https://github.com/Tina92) --- @@ -59,7 +59,7 @@ 当你明确了你想要解决的问题并有了一个解决方案的时候,你可能会问自己这是不是一个好的点子。 -**相当一部分看起来很糟糕的点子最后被证明是超级棒的生意。** 比如,当年没人想投资 Airbnb。 Brian Chesky, Airbnb 的创始人之一,在他的文章 [7 Rejections](https://medium.com/@bchesky/7-rejections-7d894cbaa084#.l8fdqlasz) 里详细讲述了被投资人拒绝的故事。 +**相当一部分看起来很糟糕的点子最后却造就了一些伟大的公司。** 比如,当年没人想投资 Airbnb。 Brian Chesky, Airbnb 的创始人之一,在他的文章 [7 Rejections](https://medium.com/@bchesky/7-rejections-7d894cbaa084#.l8fdqlasz) 里详细讲述了被投资人拒绝的故事。 ![](https://cdn-images-1.medium.com/max/800/1*WpxUxMCO-7NXr-o1yo023g.png) From b30c68efa9815d4652b09acc573ffbbb3854c38a Mon Sep 17 00:00:00 2001 From: sqrtthree Date: Fri, 14 Apr 2017 10:33:33 +0800 Subject: [PATCH 167/638] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=97=A0=E7=94=A8?= =?UTF-8?q?=E7=9A=84=E5=9B=BE=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO/debugging-tips-tricks.md | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/TODO/debugging-tips-tricks.md b/TODO/debugging-tips-tricks.md index dca2d0b0200..dee46a5eabc 100644 --- a/TODO/debugging-tips-tricks.md +++ b/TODO/debugging-tips-tricks.md @@ -4,18 +4,15 @@ > * 译者: > * 校对者: -# Debugging Tips and Tricks # +# Debugging Tips and Tricks # Writing code is only one small piece of being a developer. In order to be efficient and capable at our jobs, we must also excel at debugging. When I dedicate some time to learning new debugging skills, I often find I can move much quicker, and add more value to the teams I work on. I have a few tips and tricks I rely on pretty heavily and found that I give the same advice again and again during workshops, so here’s a compilation of some of them, as well as some from the community. We'll start with some core tenants and then drill down to more specific examples. -[![](https://s3.buysellads.com/1279518/7386844-1491489478.jpg)](https://srv.buysellads.com/ads/click/x/GTND4237CVYDL23JCV74YKQWF67DTKJNCT7DKZ3JCEAI45QUCT7D6K7KC6BDLKJEF67DTK3EHJNCLSIZ) - - ### # Main Concepts ### #### # Isolate the Problem #### -Isolation is possibly the strongest core tenant in all of debugging. Our codebases can be sprawling, with different libraries, frameworks, and they can include many contributors, even people who aren't working on the project anymore. Isolating the problem helps us slowly whittle away non-essential parts of the problem so that we can singularly focus on a solution. +Isolation is possibly the strongest core tenant in all of debugging. Our codebases can be sprawling, with different libraries, frameworks, and they can include many contributors, even people who aren't working on the project anymore. Isolating the problem helps us slowly whittle away non-essential parts of the problem so that we can singularly focus on a solution. Some of the benefits of isolation include, but are not limited to: @@ -36,7 +33,7 @@ You can take this one step further in Chrome DevTools and even walk through the Good 'ol `console.log` is a form of isolation. (Or `echo` in PHP, or `print` in python, etc…). You are taking one tiny piece of execution and testing your assumptions, or checking to see if something is altering. This is probably the most time-tested form of debugging, that no matter how advanced you become, still has it's uses. Arrow functions in ES6 have allowed us to step up our console debugging game as well, as it’s now a lot easier to write useful one-liners in the console. -The `console.table` function is also a favorite tool of mine, especially great for when you have a lot of data you need to represent- large arrays, large objects and the like. The `console.dir` function is also a nice alternative. It will log an interactive listing of an object's properties. +The `console.table` function is also a favorite tool of mine, especially great for when you have a lot of data you need to represent- large arrays, large objects and the like. The `console.dir` function is also a nice alternative. It will log an interactive listing of an object's properties. ![](https://cdn.css-tricks.com/wp-content/uploads/2017/04/dir.png) @@ -93,11 +90,11 @@ The CSS tracker shows color-coded rules for used and unused sets. #### # Misc Tools #### -- [What input](https://ten1seven.github.io/what-input/) is a global utility for tracking the current input method (mouse, keyboard or touch), as well as the current intent- this can be really good for tracking down accessiblity leaks (hat tip to Marcy Sutton, accessibility expert for this tipoff) +- [What input](https://ten1seven.github.io/what-input/) is a global utility for tracking the current input method (mouse, keyboard or touch), as well as the current intent- this can be really good for tracking down accessiblity leaks (hat tip to Marcy Sutton, accessibility expert for this tipoff) - [Ghostlabapp](https://www.vanamco.com/ghostlab/) is a pretty snazzy tool if you’re doing responsive development or checking anything deployed across a ton of devices. It offers synchronized web development, testing, and inspection. - [Eruda is an awesome tool](http://eruda.liriliri.io/) that helps debug on mobile devices. I really like it because it takes a simulator a step further, gives a console and real devtools to help you gain understanding. -![eruda gives you a mobile console](https://cdn.css-tricks.com/wp-content/uploads/2017/04/Screen-Shot-2017-04-10-at-10.38.57-AM.png) +![eruda gives you a mobile console](https://cdn.css-tricks.com/wp-content/uploads/2017/04/Screen-Shot-2017-04-10-at-10.38.57-AM.png) ### # Specific Tips ### @@ -119,7 +116,7 @@ $('body').on('focusin',function(){ We got quite a lot of responses saying that people put red borders on elements to see what they’re doing > [@sarah_edo](https://twitter.com/sarah_edo) for CSS, I'll usually have a .debug class with a red border that I slap on troublesome elements. -> +> > — Jeremy Wagner (@malchata) [March 15, 2017](https://twitter.com/malchata/status/842029469246324736) I do this too, I even have a little CSS file that drops in some classes I can access for different colors easily. @@ -127,7 +124,7 @@ I do this too, I even have a little CSS file that drops in some classes I can ac #### # Checking State in React #### > [@sarah_edo](https://twitter.com/sarah_edo)
{JSON.stringify(this.state, null, 2)}
-> +> > — MICHAEL JACKSON (@mjackson) [March 15, 2017](https://twitter.com/mjackson/status/842041642760646657) Props to Michael, this is one of the most useful debugging tools I know of. That snippet "pretty prints" the state of the component you're working with onto the component so that you can see what’s going on. You can validate that the state is working the way that you think it should be, and it helps track down any errors between the state and how you're using it. @@ -137,7 +134,7 @@ Props to Michael, this is one of the most useful debugging tools I know of. That We got a lot of responses that said they slow the animation way down: > [@sarah_edo](https://twitter.com/sarah_edo)[@Real_CSS_Tricks](https://twitter.com/Real_CSS_Tricks) * { animation-duration: 10s !important; } -> +> > — Thomas Fuchs (@thomasfuchs) [March 15, 2017](https://twitter.com/thomasfuchs/status/842029720820695040) I mentioned this on a post I wrote right here on CSS Tricks about [debugging CSS Keyframe animations](https://css-tricks.com/debugging-css-keyframe-animations/), there are more tips too, like how to hardware accelerate, or work with multiple transforms in different percentages. @@ -157,7 +154,7 @@ I tend to work on fast connections, so I will throttle my connection to check an This is also useful in conjunction with a hard reload, or with the cache empty > [@sarah_edo](https://twitter.com/sarah_edo) Not so secret trick. But still many people are unaware. You need DevTools open, and then right click over the refresh button. [pic.twitter.com/FdAfF9Xtxm](https://t.co/FdAfF9Xtxm) -> +> > — David Corbacho (@dcorbacho) [March 15, 2017](https://twitter.com/dcorbacho/status/842033259664035840) #### # Set a Timed Debugger #### @@ -175,13 +172,13 @@ It’s similar to the debugger; tool I mentioned earlier, except you can put it #### # Simulators #### > [@Real_CSS_Tricks](https://twitter.com/Real_CSS_Tricks) And just in case any Mac users didn't know this, iOS simulator + Safari is sweet. [pic.twitter.com/Uz4XO3e6uD](https://t.co/Uz4XO3e6uD) -> +> > — Chris Coyier (@chriscoyier) [March 15, 2017](https://twitter.com/chriscoyier/status/842034009060302848) I mentioned simulators with Eruda before. iOS users also get a pretty sweet simulator. I was going to tell you you have to install XCode first, but this tweet showed another way: > [@chriscoyier](https://twitter.com/chriscoyier)[@Real_CSS_Tricks](https://twitter.com/Real_CSS_Tricks) Or, you can use this approach if you didn't want to bother with installing xCode: [https://t.co/WtAnZNo718](https://t.co/WtAnZNo718) -> +> > — Chris Harrison (@cdharrison) [March 15, 2017](https://twitter.com/cdharrison/status/842038887904088065) Chrome also has a device toggle which is helpful. @@ -189,7 +186,7 @@ Chrome also has a device toggle which is helpful. #### # Remote Debuggers #### > [@chriscoyier](https://twitter.com/chriscoyier)[@Real_CSS_Tricks](https://twitter.com/Real_CSS_Tricks)[https://t.co/q3OfWKNlUo](https://t.co/q3OfWKNlUo) is a good tool. -> +> > — Gilles 💾⚽ (@gfra54) [March 15, 2017](https://twitter.com/gfra54/status/842035375304523777) I actually didn't know about this tool until seeing this tweet. Pretty useful! @@ -198,7 +195,7 @@ I actually didn't know about this tool until seeing this tweet. Pretty useful! Rachel Andrew gave a presentation at Smashing and mentioned a little waffle thing you can click on in Firefox that will illuminate the gutters in the grid. [Her video](http://gridbyexample.com/learn/2016/12/17/learning-grid-day17/) explains it really eloquently. -![](https://cdn.css-tricks.com/wp-content/uploads/2017/04/Screen-Shot-2017-04-10-at-9.58.14-AM.png) +![](https://cdn.css-tricks.com/wp-content/uploads/2017/04/Screen-Shot-2017-04-10-at-9.58.14-AM.png) Rachel Andrew shows how to highlight gutters in Firefox DevTools. @@ -207,14 +204,14 @@ Rachel Andrew shows how to highlight gutters in Firefox DevTools. Wes Bos with a really useful tip for searching for a single item in an array: > If you are just looking for a single item array.find() is 🔥 [https://t.co/AuRtyFwnq7](https://t.co/AuRtyFwnq7) -> +> > — Wes Bos (@wesbos) [March 15, 2017](https://twitter.com/wesbos/status/842069915158884354) ### # Further Debugging Resources ### Jon Kuperman has a [Frontend Masters course](https://frontendmasters.com/courses/chrome-dev-tools/) that can help you master devtools it goes along [with this app](https://github.com/jkup/mastering-chrome-devtools). -There’s a small course on code school called [discover devtools](https://www.codeschool.com/courses/discover-devtools). +There’s a small course on code school called [discover devtools](https://www.codeschool.com/courses/discover-devtools). Umar Hansa has a new online course called [Modern DevTools](https://moderndevtools.com/). From d7f45df5a6af8f59194ced27863aa89ceaa8c12b Mon Sep 17 00:00:00 2001 From: reid Date: Fri, 14 Apr 2017 14:14:05 +0800 Subject: [PATCH 168/638] =?UTF-8?q?=E6=A0=B9=E6=8D=AE=E6=A0=A1=E5=AF=B9?= =?UTF-8?q?=E6=84=8F=E8=A7=81=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO/higher-order-functions-composing-software.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO/higher-order-functions-composing-software.md b/TODO/higher-order-functions-composing-software.md index 67336f3fd47..0f8e8def2bf 100644 --- a/TODO/higher-order-functions-composing-software.md +++ b/TODO/higher-order-functions-composing-software.md @@ -127,7 +127,7 @@ const gt3 = highpass(3); [1, 2, 3, 4].filter(gt3); // [3, 4]; ``` -换言之,高阶函数可以用来实现函数的多态性。如你所见,相较于一阶函数而言,高阶函数的复用性和通用性非常好。一般来讲,在实际编码中会组合使用高阶函数和一些非常简单的一阶函数。 +换言之,高阶函数可以用来实现函数的多态性。如你所见,相对于一阶函数而言,高阶函数的复用性和通用性更好。一般来讲,在实际编码中会组合使用高阶函数和一些非常简单的一阶函数。 [**再续 “Reduce” >**](https://medium.com/javascript-scene/reduce-composing-software-fe22f0c39a1d) From 966426c87034b8bca35ff74ff6733d14a4149da2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=B9=E5=8F=B7=E4=B8=89?= Date: Fri, 14 Apr 2017 20:49:01 +0800 Subject: [PATCH 169/638] Update debugging-tips-tricks.md --- TODO/debugging-tips-tricks.md | 42 +++++++++++++++++------------------ 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/TODO/debugging-tips-tricks.md b/TODO/debugging-tips-tricks.md index dee46a5eabc..12b0aec52df 100644 --- a/TODO/debugging-tips-tricks.md +++ b/TODO/debugging-tips-tricks.md @@ -8,9 +8,9 @@ Writing code is only one small piece of being a developer. In order to be efficient and capable at our jobs, we must also excel at debugging. When I dedicate some time to learning new debugging skills, I often find I can move much quicker, and add more value to the teams I work on. I have a few tips and tricks I rely on pretty heavily and found that I give the same advice again and again during workshops, so here’s a compilation of some of them, as well as some from the community. We'll start with some core tenants and then drill down to more specific examples. -### # Main Concepts ### +### Main Concepts ### -#### # Isolate the Problem #### +#### Isolate the Problem #### Isolation is possibly the strongest core tenant in all of debugging. Our codebases can be sprawling, with different libraries, frameworks, and they can include many contributors, even people who aren't working on the project anymore. Isolating the problem helps us slowly whittle away non-essential parts of the problem so that we can singularly focus on a solution. @@ -39,7 +39,7 @@ The `console.table` function is also a favorite tool of mine, especially great f console.dir gives an interactive listing -#### #Be Methodical #### +#### Be Methodical #### When I teach workshops and help students in my class, the number one thing that I find holds them back as they try to debug a problem is not being methodical enough. This is truly a tortoise-and-the-hare kind of situation. They understandably want to move quickly, so they change a ton of things at once, and then when something stops working, they don’t know which thing they changed is causing the error. Then, to debug, they change many things at once and get a little lost trying to figure out what is working and what isn't. @@ -47,15 +47,15 @@ We all do this to some extent. As we become more proficient with a tool, we can **Do you remember when you were a kid and your parents said, "if you get lost, stay where you are?"** My parents did, at least. It's because if they were moving around to find me and I was also moving around to find them, we'd have fewer chances of bumping into one another. Code works the same way. The less moving pieces you have, the better- the more you are returning consistent results, the easier it will be to track things down. So while you’re debugging, try not to also install anything, or put in new dependencies. If you see a different error every time you should be returning a static result, that’s a big red flag you should be headed right for with your sleuth hat on. -### # Choose Good Tools ### +### Choose Good Tools ### There are a million different tools for solving a variety of problems. I’m going to work through some of the tools I find the most useful and then we’ll link off to a bevy of resources. -#### # Syntax Highlighting #### +#### Syntax Highlighting #### Sure, it’s damn fun to pick out the new hotness in colors and flavors for your syntax highlighting theme, but spending some time thinking about clarity here matters. I often pick dark themes where a skip in syntax will turn all of my code a lighter color, I find errors are really easy to see right away. I tend to like Oceanic Next or Panda, but really, to each their own on this one. It’s important to keep in mind that when looking for a good syntax highlighter, awesome-looking is great, but functional for calling out your mistakes is most important, and it's totally possible to do both. -#### # Linting #### +#### Linting #### Linting helps flag suspicious code and calls out errors we might have overlooked. Linting is incredibly important, but which linter you choose has so much to do with what language/framework you’re writing in, and then on top of that, what your agreed-upon code style is. @@ -66,7 +66,7 @@ Here are some resources: - I recently found [this responsive images linter](https://github.com/ausi/respimagelint), that tells you what opportunities you might have to use picture, srcset, or sizes. - Here’s a [pretty good breakdown](https://www.sitepoint.com/comparison-javascript-linting-tools/) of some JS linters -#### # Browser Extensions #### +#### Browser Extensions #### Extensions can be really awesome because they can be enabled and disabled so readily, and they can work with really specific requirements. If you’re working with a particular library or framework, chances are, having their extension for DevTools enabled is going to give you all sorts of clarity that you can’t find otherwise. Take care though- not only can extensions bog a browser down, but they have permissions to execute scripts, so do a little homework into the extension author, ratings, and background. All that said, here are some of my favorites: @@ -76,7 +76,7 @@ Extensions can be really awesome because they can be enabled and disabled so rea - [Codopen](https://chrome.google.com/webstore/detail/codopen/agnkphdgffianchpipdbkeaclfbobaak): pops you out of the editor mode into a debug window for CodePen. Full disclosure: my husband made this for me as a present because he was sick of watching me manually opening the debug window (best gift ever!) - [Pageruler](https://chrome.google.com/webstore/detail/page-ruler/jlpkojjdgbllmedoapgfodplfhcbnbpn): get pixel dimensions and measure anything on a page. I like this one because I’m super duper anal about my layout. This helps me feed the beast. -### # DevTools ### +### DevTools ### This is probably the most obvious of debugging tools, and there are so many things you can do with them. They can have so many packed-in features that can be easy to miss, so in the next section of specific tips, we'll go into a deep dive of some favorites. @@ -88,7 +88,7 @@ One of my favorite recent ones is this [CSS Tracker Enhancement](https://umaar.c The CSS tracker shows color-coded rules for used and unused sets. -#### # Misc Tools #### +#### Misc Tools #### - [What input](https://ten1seven.github.io/what-input/) is a global utility for tracking the current input method (mouse, keyboard or touch), as well as the current intent- this can be really good for tracking down accessiblity leaks (hat tip to Marcy Sutton, accessibility expert for this tipoff) - [Ghostlabapp](https://www.vanamco.com/ghostlab/) is a pretty snazzy tool if you’re doing responsive development or checking anything deployed across a ton of devices. It offers synchronized web development, testing, and inspection. @@ -96,11 +96,11 @@ The CSS tracker shows color-coded rules for used and unused sets. ![eruda gives you a mobile console](https://cdn.css-tricks.com/wp-content/uploads/2017/04/Screen-Shot-2017-04-10-at-10.38.57-AM.png) -### # Specific Tips ### +### Specific Tips ### I am always interested in what other people do to debug, so I asked the community through the CSS-Tricks account and my own what they were really into. This list is a mixture of tips I like as well as a roundup of tips from the community. -#### # Accessibility #### +#### Accessibility #### ``` $('body').on('focusin',function(){ @@ -111,7 +111,7 @@ $('body').on('focusin',function(){ -[Marcy Sutton](https://twitter.com/marcysutton) -#### # Debugging CSS #### +#### Debugging CSS #### We got quite a lot of responses saying that people put red borders on elements to see what they’re doing @@ -121,7 +121,7 @@ We got quite a lot of responses saying that people put red borders on elements t I do this too, I even have a little CSS file that drops in some classes I can access for different colors easily. -#### # Checking State in React #### +#### Checking State in React #### > [@sarah_edo](https://twitter.com/sarah_edo)
{JSON.stringify(this.state, null, 2)}
> @@ -129,7 +129,7 @@ I do this too, I even have a little CSS file that drops in some classes I can ac Props to Michael, this is one of the most useful debugging tools I know of. That snippet "pretty prints" the state of the component you're working with onto the component so that you can see what’s going on. You can validate that the state is working the way that you think it should be, and it helps track down any errors between the state and how you're using it. -#### # Animation #### +#### Animation #### We got a lot of responses that said they slow the animation way down: @@ -145,7 +145,7 @@ I also slow down my animations in JavaScript- in GreenSock that would look like: If you want to use the Chrome Devtools timeline to do performance audits, it's worth mentioning that painting is the most expense of the tasks, so all things being equal, pay a little more attention to a high percentage of that green. -#### # Checking different connection speeds and loads #### +#### Checking different connection speeds and loads #### I tend to work on fast connections, so I will throttle my connection to check and see what the performance would look like for people who don’t have my internet speed. @@ -157,7 +157,7 @@ This is also useful in conjunction with a hard reload, or with the cache empty > > — David Corbacho (@dcorbacho) [March 15, 2017](https://twitter.com/dcorbacho/status/842033259664035840) -#### # Set a Timed Debugger #### +#### Set a Timed Debugger #### This one came from Chris. We have a whole writeup on it [right here](https://css-tricks.com/set-timed-debugger-web-inspect-hard-grab-elements/): @@ -169,7 +169,7 @@ setTimeout(function() { It’s similar to the debugger; tool I mentioned earlier, except you can put it in a setTimeout function and get even more fine-tuned information -#### # Simulators #### +#### Simulators #### > [@Real_CSS_Tricks](https://twitter.com/Real_CSS_Tricks) And just in case any Mac users didn't know this, iOS simulator + Safari is sweet. [pic.twitter.com/Uz4XO3e6uD](https://t.co/Uz4XO3e6uD) > @@ -183,7 +183,7 @@ I mentioned simulators with Eruda before. iOS users also get a pretty sweet simu Chrome also has a device toggle which is helpful. -#### # Remote Debuggers #### +#### Remote Debuggers #### > [@chriscoyier](https://twitter.com/chriscoyier)[@Real_CSS_Tricks](https://twitter.com/Real_CSS_Tricks)[https://t.co/q3OfWKNlUo](https://t.co/q3OfWKNlUo) is a good tool. > @@ -191,7 +191,7 @@ Chrome also has a device toggle which is helpful. I actually didn't know about this tool until seeing this tweet. Pretty useful! -#### # CSS Grid Debugging #### (#article-header-id-18 .has-header-link) +#### CSS Grid Debugging #### (#article-header-id-18 .has-header-link) Rachel Andrew gave a presentation at Smashing and mentioned a little waffle thing you can click on in Firefox that will illuminate the gutters in the grid. [Her video](http://gridbyexample.com/learn/2016/12/17/learning-grid-day17/) explains it really eloquently. @@ -199,7 +199,7 @@ Rachel Andrew gave a presentation at Smashing and mentioned a little waffle thin Rachel Andrew shows how to highlight gutters in Firefox DevTools. -#### # Array Debugging #### +#### Array Debugging #### Wes Bos with a really useful tip for searching for a single item in an array: @@ -207,7 +207,7 @@ Wes Bos with a really useful tip for searching for a single item in an array: > > — Wes Bos (@wesbos) [March 15, 2017](https://twitter.com/wesbos/status/842069915158884354) -### # Further Debugging Resources ### +### Further Debugging Resources ### Jon Kuperman has a [Frontend Masters course](https://frontendmasters.com/courses/chrome-dev-tools/) that can help you master devtools it goes along [with this app](https://github.com/jkup/mastering-chrome-devtools). From 74a1cd716a7c51e6ed8e88571222cc79905207bc Mon Sep 17 00:00:00 2001 From: zhuzi Date: Sat, 15 Apr 2017 15:04:50 +0800 Subject: [PATCH 170/638] translation completed --- TODO/secure-web-app-http-headers.md | 40 +++++++++++++++++------------ 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/TODO/secure-web-app-http-headers.md b/TODO/secure-web-app-http-headers.md index d15fe288045..9e1716b7e1f 100644 --- a/TODO/secure-web-app-http-headers.md +++ b/TODO/secure-web-app-http-headers.md @@ -292,39 +292,45 @@ functionrequestHandler(req, res){ res.setHeader('Content-Security-Policy',"script-src 'self'");} ``` -### Preventing Content-Type Sniffing ### +### 防止 Content-Type 嗅探 ### -In an effort to make the user experience as seamless as possible, many browsers have implemented a feature called content-type sniffing, or MIME sniffing. This feature enables the browser to detect the type of a resource provided as part of an HTTP response by “sniffing” the actual resource bits, regardless of the resource type declared through the `Content-Type` response header. While this feature is indeed useful in some cases, it introduces a vulnerability and an attack vector known as a MIME confusion attack. A MIME-sniffing vulnerability enables an attacker to inject a malicious resource, such as a malicious executable script, masquerading as an innocent resource, such as an image. With MIME sniffing, the browser will ignore the declared image content type, and instead of rendering an image will execute the malicious script. +> In an effort to make the user experience as seamless as possible, many browsers have implemented a feature called content-type sniffing, or MIME sniffing. This feature enables the browser to detect the type of a resource provided as part of an HTTP response by “sniffing” the actual resource bits, regardless of the resource type declared through the `Content-Type` response header. While this feature is indeed useful in some cases, it introduces a vulnerability and an attack vector known as a MIME confusion attack. A MIME-sniffing vulnerability enables an attacker to inject a malicious resource, such as a malicious executable script, masquerading as an innocent resource, such as an image. With MIME sniffing, the browser will ignore the declared image content type, and instead of rendering an image will execute the malicious script. -Luckily, the `X-Content-Type-Options` response header mitigates this vulnerability! This header, introduced in Internet Explorer 8 back in 2008 and currently supported by most major browsers (Safari is the only major browser not to support it), instructs the browser not to use sniffing when handling fetched resources. Because `X-Content-Type-Options` was only formally specified as part of the [“Fetch” specification](https://fetch.spec.whatwg.org/#x-content-type-options-header), the actual implementation varies across browsers; some (Internet Explorer and Edge) completely avoid MIME sniffing, whereas others (Firefox) still MIME sniff but rather block executable resources (JavaScript and CSS) when an inconsistency between declared and actual types is detected. The latter is in line with the latest Fetch specification. +为了使用户体验尽可能无缝连接,许多浏览器实现了一个功能叫 内容类型嗅探,或者 MIME 嗅探。这个功能使得浏览器可以通过「嗅探」实际 HTTP 响应的资源的内容直接检测到资源的类型,无论在响应头中 `Content-Type` 是如何指定资源类型的。虽然这个功能在某些情况下确实是有用的,它引入了一个漏洞以及一种叫 MIME 混乱攻击。MIME 嗅探漏洞使攻击者可以注入恶意资源,例如恶意脚本,伪装成一个无辜的资源,例如一个图片。通过 MIME 嗅探,浏览器将忽略声明的图像内容类型,它不会渲染图片,而是执行恶意脚本。 -`X-Content-Type-Options` is a simple response header, with only one directive: `nosniff`. This header looks like this: `X-Content-Type-Options: nosniff`. Here’s an example of a configuration of the header: +> Luckily, the `X-Content-Type-Options` response header mitigates this vulnerability! This header, introduced in Internet Explorer 8 back in 2008 and currently supported by most major browsers (Safari is the only major browser not to support it), instructs the browser not to use sniffing when handling fetched resources. Because `X-Content-Type-Options` was only formally specified as part of the [“Fetch” specification](https://fetch.spec.whatwg.org/#x-content-type-options-header), the actual implementation varies across browsers; some (Internet Explorer and Edge) completely avoid MIME sniffing, whereas others (Firefox) still MIME sniff but rather block executable resources (JavaScript and CSS) when an inconsistency between declared and actual types is detected. The latter is in line with the latest Fetch specification. -``` +幸运的是,`X-Content-Type-Options` 响应头缓解了这个漏洞。此响应头在 2008 年引入 IE8,目前大多数主流浏览器都支持(Safari 是唯一不支持的主流浏览器),它指示浏览器在处理获取的资源时不使用嗅探。因为 `X-Content-Type-Options` 仅在 [「Fetch」规范](https://fetch.spec.whatwg.org/#x-content-type-options-header)中正式指定,实际的实现因浏览器而异。一部分浏览器(IE 和 Edge)完全阻止了 MIME 嗅探,而其他一些(Firefox)仍然会进行 MIME 嗅探,但会屏蔽掉可执行的资源(JavaScript 和 CSS)如果声明的内容类型与实际的类型不一致。后者符合最新的 Fetch 规范。 + +> `X-Content-Type-Options` is a simple response header, with only one directive: `nosniff`. This header looks like this: `X-Content-Type-Options: nosniff`. Here’s an example of a configuration of the header: + +`X-Content-Type-Options` 是一个很简单的响应头,它只有一个指令,`nosniff`。它是这样指定的:`X-Content-Type-Options: nosniff`。以下是示例代码: + +```javascript functionrequestHandler(req, res){ res.setHeader('X-Content-Type-Options','nosniff');} ``` -### Summary ### +### 总结 ### -In this article, we have seen how to leverage HTTP headers to reinforce the security of your web app, to fend off attacks and to mitigate vulnerabilities. +> In this article, we have seen how to leverage HTTP headers to reinforce the security of your web app, to fend off attacks and to mitigate vulnerabilities. -#### Takeaways #### +本文中,我们了解了如何利用 HTTP 响应头来加强 web 应用的安全性,防止攻击和减轻漏洞。 -- Disable caching for confidential information using the `Cache-Control` header. -- Enforce HTTPS using the `Strict-Transport-Security` header, and add your domain to Chrome’s preload list. -- Make your web app more robust against XSS by leveraging the `X-XSS-Protection` header. -- Block clickjacking using the `X-Frame-Options` header. -- Leverage `Content-Security-Policy` to whitelist specific sources and endpoints. -- Prevent MIME-sniffing attacks using the `X-Content-Type-Options` header. +#### 要点 #### -Remember that for the web to be truly awesome and engaging, it has to be secure. Leverage HTTP headers to build a more secure web! +- 使用 `Cache-Control` 禁用对机密信息的缓存 +- 通过 `Strict-Transport-Security` 强制使用 HTTPS,并将你的域添加到 Chrome 预加载列表 +- 利用 `X-XSS-Protection` 使你的 web 应用更加能抵抗 XSS 攻击 +- 使用 `X-Frame-Options` 阻止点击劫持 +- 利用 `Content-Security-Policy` 将特定来源于端点列入白名单 +- 使用 `X-Content-Type-Options` 防止 MIME 嗅探攻击 +请记住,为了使 web 真正迷人,它必须是安全的。利用 HTTP 响应头构建更加安全的网页吧! -(**Disclaimer:** The content of this post is my own and doesn’t represent my past or current employers in any way whatsoever.) -*Front page image credits: [Pexels.com](https://www.pexels.com/photo/coffee-writing-computer-blogging-34600/).* +(**声明:** 此文内容仅属本人,不代表本人过去或现在的雇主。) --- From 08853d8784ccbda58465da8b99dac09a020019b6 Mon Sep 17 00:00:00 2001 From: zhuzi Date: Sat, 15 Apr 2017 16:03:58 +0800 Subject: [PATCH 171/638] reviewed --- TODO/secure-web-app-http-headers.md | 148 ++++++---------------------- 1 file changed, 30 insertions(+), 118 deletions(-) diff --git a/TODO/secure-web-app-http-headers.md b/TODO/secure-web-app-http-headers.md index 9e1716b7e1f..7cf3e757680 100644 --- a/TODO/secure-web-app-http-headers.md +++ b/TODO/secure-web-app-http-headers.md @@ -6,21 +6,13 @@ ## 如何使用 HTTP Headers 来保护你的 Web 应用 ## -> Web applications, be they thin websites or thick single-page apps, are notorious targets for cyber-attacks. In 2016, approximately [40% of data breaches](http://www.verizonenterprise.com/verizon-insights-lab/dbir/2016/) originated from attacks on web apps — the leading attack pattern. Indeed, these days, understanding cyber-security is not a luxury but rather **a necessity for web developers**, especially for developers who build consumer-facing applications. +Web 应用,无论是简单的小网页还是复杂的单页应用,众所周知都是网络攻击的目标。2016年,大约 40% 的数据泄露源自对 Web 应用的攻击,这是主要的攻击模式。事实上,现在来说,了解网络安全并不是锦上添花, 而是 **Web 开发者的必需任务**,特别对于构建面向消费者的产品的开发人员。 -Web 应用,无论是简单的小网页还是复杂的单页应用,众所周知都是网络攻击的目标。2016年,大约 40% 的数据泄露源自对 Web 应用的攻击,这是主要的攻击模式。事实上,现在来说,了解网络安全并不是锦上添花, 而是 Web 开发者的必需任务,特别对于构建面向消费者的产品的开发人员。 - -> HTTP response headers can be leveraged to tighten up the security of web apps, typically just by adding a few lines of code. In this article, we’ll show how web developers can use HTTP headers to build secure apps. While the code examples are for Node.js, setting HTTP response headers is supported across all major server-side-rendering platforms and is typically simple to set up. - -开发者可以利用 HTTP 响应头来加强 Web 应用程序的安全性,通常只需要添加几行代码即可。本文将结束 web 开发者如何利用 HTTP Headers 来构建安全的应用。虽然本文的示例代码是 Node.js,但是设置 HTTP 响应头基本在所有主要的服务端语言都是简单易设置的。 +开发者可以利用 HTTP 响应头来加强 Web 应用程序的安全性,通常只需要添加几行代码即可。本文将介绍 web 开发者如何利用 HTTP Headers 来构建安全的应用。虽然本文的示例代码是 Node.js,但是设置 HTTP 响应头基本在所有主要的服务端语言中都是简单易设置的。 ### 关于 HTTP Headers ### -> Technically, HTTP headers are simply fields, encoded in clear text, that are part of the HTTP request and response message header. They are designed to enable both the HTTP client and server to send and receive meta data about the connection to be established, the resource being requested, as well as the returned resource itself. - -技术上,HTTP 头只是简单的字段,咦明文形式编码,这是 HTTP 请求和响应消息头的一部分。它们旨在使客户端和服务端都能够发送和接受有关要建立的连接元数据、所请求的资源,以及返回的资源本身的元数据。 - -> Plain-text HTTP response headers can be examined easily using cURL, with the `--head` option, like so: +技术上来说,HTTP 头只是简单的字段,以明文形式编码,它是 HTTP 请求和响应消息头的一部分。它们旨在使客户端和服务端都能够发送和接受有关要建立的连接、所请求的资源,以及返回的资源本身的元数据。 可以使用 cURL `--head` 选项轻松检查纯文本 HTTP 响应头,例如: @@ -37,48 +29,30 @@ Vary: Accept-Encoding … ``` -> Today, hundreds of headers are used by web apps, some standardized by the [Internet Engineering Task Force](https://www.ietf.org/) (IETF), the open organization that is behind many of the standards that power the web as we know it today, and some proprietary. HTTP headers provide a flexible and extensible mechanism that enables the rich and varying use cases found on the web today. - -现在,数百种响应头正在被 web 应用所使用,其中一部分由[互联网工程任务组, IETF](https://www.ietf.org/)标准化。IETF 是一个开发性组织,今天我们所熟知的许多 web 标准或专利都是由他们进行推进的。HTTP 头提供了一种灵活可扩展的机制,造就了现今的网络各种丰富多变的用例。 +现在,数百种响应头正在被 web 应用所使用,其中一部分由[互联网工程任务组(IETF)](https://www.ietf.org/)标准化。IETF 是一个开发性组织,今天我们所熟知的许多 web 标准或专利都是由他们推进的。HTTP 头提供了一种灵活可扩展的机制,造就了现今的网络各种丰富多变的用例。 ### 机密资源禁用缓存 ### -> Caching is a valuable and effective technique for optimizing performance in client-server architectures, and HTTP, which leverages caching extensively, is no exception. However, in cases where the cached resource is confidential, caching can lead to vulnerabilities — and must be avoided. As an example, consider a web app that renders and caches a page with sensitive information and is being used on a shared PC. Anyone can view confidential information rendered by that web app simply by visiting the browser’s cache, or sometimes even as easily as clicking the browser’s “back” button! - -缓存是优化客户端-服务端架构性能中有效的技术,广泛利用缓存的 HTTP 也不例外。但是,在缓存的资源是保密的情况下,缓存可能导致漏斗,所以必须避免。假设一个 web 应用对含有敏感信息的网页进行缓存,并且是在一台公用的 PC 上使用。任何人可以通过访问浏览器的缓存看到这个 web 应用上的敏感信息,甚至有时仅仅通过点击浏览器的返回按钮就可以看到。 +缓存是优化客户端-服务端架构性能中有效的技术,广泛利用缓存的 HTTP 并不少见。但是,在缓存的资源是保密的情况下,缓存可能导致漏洞,所以必须避免。假设一个 web 应用对含有敏感信息的网页进行缓存,并且是在一台公用的 PC 上使用,任何人可以通过访问浏览器的缓存看到这个 web 应用上的敏感信息,甚至有时仅仅通过点击浏览器的返回按钮就可以看到。 -> The IETF’s [RFC 7234](https://tools.ietf.org/html/rfc7234), which defines HTTP caching, specifies the default behavior of HTTP clients, both browsers and intermediary Internet proxies, to *always* cache responses to HTTP `GET` requests — unless specified otherwise. While this enables HTTP to boost performance and reduce network congestion, it could also expose end users to theft of personal information, as mentioned above. The good news is that the HTTP specification also defines a pretty simple way to instruct clients not to cache a given response, through the use of — you guessed it! — HTTP response headers. +IETF [RFC 7234](https://tools.ietf.org/html/rfc7234) 上定义了 HTTP 缓存,指定 HTTP 客户端(浏览器以及网络代理)的默认行为,也就是始终缓存对 HTTP GET 请求的响应,除非另行指定。虽然这样可以使 HTTP 提升性能减少网络拥塞,但如上所述,它也有可能使终端用户个人信息被盗。好消息是,HTTP 规范还定义了一种非常简单的方式来指示客户端对特定响应不进行缓存,通过使用 —— 对,你猜到了 —— HTTP 响应头。 -IETF 的 [RFC 7234](https://tools.ietf.org/html/rfc7234) 上定义了 HTTP 缓存,指定 HTTP 客户端(浏览器以及网络代理)的默认行为,也就是始终缓存对 HTTP GET 请求的相应,除非另行指定。虽然这样可以使 HTTP 提升性能减少网络拥塞,但如上所述,它也有可能使终端用户个人信息被盗。好消息是,HTTP 规范还廷议了一种非常简单的方式来指示客户端对特定响应不进行缓存,通过使用 —— 对,你猜到了 —— HTTP 响应头。 - -> There are three headers to return when you are returning sensitive information and would like to disable caching by HTTP clients: - -当你返回敏感信息并希望禁用 HTTP 客户端的缓存时,有三个头可以返回: +当你返回敏感信息并希望禁用 HTTP 客户端的缓存时,有三个响应头可以返回: - `Cache-Control` -> This response header, introduced in HTTP 1.1, may contain one or more directives, each carrying a specific caching semantic, and instructing HTTP clients and proxies on how to treat the response being annotated by the header. My recommendation is to format the header as follows: `cache-control: no-cache, no-store, must-revalidate`. These three directives pretty much instruct clients and intermediary proxies not to use a previously cached response, not to store the response, and that even if the response is somehow cached, the cache must be revalidated on the origin server. - 从 HTTP 1.1 引入的此响应头可能包含一个或多个指令,每个指令带有特定的缓存语义,指示 HTTP 客户端和代理如何处理有此响应头注释的响应。我推荐如下指定响应头,`cache-control: no-cache, no-store, must-revalidate`。这三个指令基本上可以指示客户端和中间代理不可使用之前缓存的响应,不可存储响应,甚至就算响应被缓存,也必须从源服务器上重新验证。 - `Pragma: no-cache` -> For backwards-compatibility with HTTP 1.0, you will want to include this header as well. Some HTTP clients, especially intermediary proxies, still might not fully support HTTP 1.1 and so will not correctly handle the `Cache-Control` header mentioned above. Use `Pragma: no-cache` to ensure that these older clients do not cache your response. - -为了与 HTTP 1.0 的向后兼容性,你还需要包含此响应头。有部分客户端,特别是中间代理,可能仍然没有完全支持 HTTP 1.1,所以不能正确处理前面提到的 `Cache-Control` 响应头,所以使用 `Pragma: no-cache` 确保较旧的客户端不缓存你的响应。 +为了向后兼容 HTTP 1.0,你还需要包含此响应头。有部分客户端,特别是中间代理,可能仍然没有完全支持 HTTP 1.1,所以不能正确处理前面提到的 `Cache-Control` 响应头,所以使用 `Pragma: no-cache` 确保较旧的客户端不缓存你的响应。 - `Expires: -1` -> This header specifies a timestamp after which the response is considered stale. By specifying `-1`, instead of an actual future time, you ensure that clients immediately treat this response as stale and avoid caching. - -此标头指定了该响应过时的时间戳。如果不指定为未来某个真实时间而指定为 `-1`,可以保证客户端立即将此响应视为过时并避免缓存。 - -> Note that, while disabling caching enhances the security of your web app and helps to protect confidential information, is does come at the price of a performance hit. Make sure to disable caching only for resources that actually require confidentiality and not just for any response rendered by your server! For a deeper dive into best practices for caching web resources, I highly recommend reading [Jake Archibald’s post](https://jakearchibald.com/2016/caching-best-practices/) on the subject. +此响应头指定了该响应过时的时间戳。如果不指定为未来某个真实时间而指定为 `-1`,可以保证客户端立即将此响应视为过期并避免缓存。 需要注意的是,禁用缓存提高安全性及保护机密资源的同时,也的确会带来性能上的折损。所以确保仅对实际需要保密性的资源禁用缓存,而不是对任何服务器的响应禁用。想要更深入了解 web 资源缓存的最佳实践,我推荐阅读 [Jake Archibald 的文章](https://jakearchibald.com/2016/caching-best-practices/)。 -> Here’s how you would program these headers in Node.js: - 下面是 Node.js 中设置响应头的示例代码: ```javascript @@ -91,49 +65,29 @@ function requestHandler(req, res) { ### 强制 HTTPS ### -> Today, the importance of HTTPS is widely recognized by the tech community. More and more web apps configure secured endpoints and are redirecting unsecure traffic to secured endpoints (i.e. HTTP to HTTPS redirects). Unfortunately, end users have yet to fully comprehend the importance of HTTPS, and this lack of comprehension exposes them to various man-in-the-middle (MitM) attacks. The typical user navigates to a web app without paying much attention to the protocol being used, be it secure (HTTPS) or unsecure (HTTP). Moreover, many users will just click past browser warnings when their browser presents a certificate error or warning! - -今天,HTTPS 的重要性已经得到了科技界的广泛认可。越来越多的 web 应用配置了安全端点,并将不安全网路重定向到安全端点(即 HTTP 重定向至 HTTPS)。不幸的是,终端用户还未完全理解 HTTPS 的重要性,这种缺乏理解使他们面临着各种中间人攻击(MitM)。典型的用户访问到一个 web 应用时,并不会注意到正在使用的网络协议是安全的(HTTPS)还是不安全的(HTTP)。 - -> The importance of interacting with web apps over a valid HTTPS connection cannot be overstated: An unsecure connection exposes the user to various attacks, which could lead to cookie theft or worse. As an example, it is not very difficult for an attacker to spoof network frames within a public Wi-Fi network and to extract the session cookies of users who are not using HTTPS. To make things even worse, even users interacting with a web app over a secured connection may be exposed to downgrade attacks, which try to force the connection to be downgraded to an unsecure connection, thus exposing the user to MitM attacks. - -通过有效的 HTTPS 连接与 web 应用进行交互的重要性怎么说都不算夸大:不安全的连接将用户暴露给各种攻击,这可能导致 cookie 被盗甚至更糟。举个例子,攻击者可以轻易在公共 Wi-Fi 网络下骗过网络帧并提起不使用 HTTPS 的用户的会话 cookie。更糟的情况是,即使用户通过安全连接与 web 永盈进行交互也可能遭受降级攻击,这种攻击试图强制将连接降级到不安全的连接,从而是用户收到中间人攻击。 - -> How can we help users avoid these attacks and better enforce the usage of HTTPS? Enter the HTTP Strict Transport Security (HSTS) header. Put simply, HSTS makes sure all communications with the origin host are using HTTPS. Specified in [RFC 6797](https://tools.ietf.org/html/rfc6797), HSTS enables a web app to instruct browsers to allow *only* HTTPS connections to the origin host, to internally redirect all unsecure traffic to secured connections, and to automatically upgrade all unsecure resource requests to be secure. +今天,HTTPS 的重要性已经得到了科技界的广泛认可。越来越多的 web 应用配置了安全端点,并将不安全网路重定向到安全端点(即 HTTP 重定向至 HTTPS)。不幸的是,终端用户还未完全理解 HTTPS 的重要性,这种缺乏理解使他们面临着各种中间人攻击(MitM)。普通用户访问到一个 web 应用时,并不会注意到正在使用的网络协议是安全的(HTTPS)还是不安全的(HTTP)。 -我们如何帮助用户避免这些攻击,并更好地实施 HTTPS 的使用呢?使用 HTTP 严格传输安全头(HSTS)。简单来说,HSTS 确保与源主机间的所有通信都使用 HTTPS。[RFC 6797](https://tools.ietf.org/html/rfc6797) 中说明了,HSTS可以使 web 应用程序指示浏览器仅允许与源主机之间的 HTTPS 连接,将所有不安全的连接内部重定向到安全连接,并自动将所有不安全的资源请求升级为安全请求。 +通过有效的 HTTPS 连接与 web 应用进行交互的重要性怎么说都不算夸大:不安全的连接将用户暴露给各种攻击,这可能导致 cookie 被盗甚至更糟。举个例子,攻击者可以轻易在公共 Wi-Fi 网络下骗过网络帧并提取不使用 HTTPS 的用户的会话 cookie。更糟的情况是,即使用户通过安全连接与 web 应用进行交互也可能遭受降级攻击,这种攻击试图强制将连接降级到不安全的连接,从而使用户收到中间人攻击。 -> HSTS directives include the following: +我们如何帮助用户避免这些攻击,并更好地推行 HTTPS 的使用呢?使用 HTTP 严格传输安全头(HSTS)。简单来说,HSTS 确保与源主机间的所有通信都使用 HTTPS。[RFC 6797](https://tools.ietf.org/html/rfc6797) 中说明了,HSTS可以使 web 应用程序指示浏览器仅允许与源主机之间的 HTTPS 连接,将所有不安全的连接内部重定向到安全连接,并自动将所有不安全的资源请求升级为安全请求。 HSTS 的指令如下: - `max-age=` ->This instructs the browser to cache this header, for this domain, for the specified number of seconds. This can ensure tightened security for a long duration! - 此项指示浏览器对此域缓存此响应头指定的秒数。这样可以保证长时间的加固安全。 - `includeSubDomains` ->This instructs the browser to apply HSTS for all subdomains of the current domain. This can be useful to cover all current and future subdomains you may have. - -此项指示浏览器对当前域的所有子域应用 HSTS,这可以用于覆盖你可能有的所有当前和未来的子域。 +此项指示浏览器对当前域的所有子域应用 HSTS,这可以用于所有当前和未来可能的子域。 - `preload` -> This is a powerful directive that forces browsers to *always* load your web app securely, even on the first hit, before the response is even received! This works by hardcoding a list of HSTS preload-enabled domains into the browser’s code. To enable the preloading feature, you need to register your domain with [HSTS Preload List Submission](https://hstspreload.org), a website maintained by Google’s Chrome team. Once registered, the domain will be prebuilt into supporting browsers to always enforce HSTS. The preload directive within the HTTP response header is used to confirm registration, indicating that the web app and domain owner are indeed interested in being on the preload list. - -这是一个强大的指令,强制浏览器始终安全加载你的 web 应用程序,即使是第一次收到响应之前加载!这是通过将启用 HSTS 预加载域的列表硬编码到浏览器的代码中实现的。要启用预加载功能,你需要在 Google Chrome 团队维护的网站 [HSTS 预加载列表提交](https://hstspreload.org)注册你的域。 - -> A word of caution: using the `preload` directive also means it cannot be easily undone, and carries an update lead time of months! While preload certainly improves your app’s security, it also means you need to be fully confident your app can support HTTPS-only! - -注意谨慎使用 `preload`,因为这意味着它不能轻易撤销,并带有几个月前的更新。虽然预加载肯定会应用程序的安全性,但也意味着你需要充分确信你的应用程序可以支持仅 HTTPS! +这是一个强大的指令,强制浏览器**始终**安全加载你的 web 应用程序,即使是第一次收到响应之前加载!这是通过将启用 HSTS 预加载域的列表硬编码到浏览器的代码中实现的。要启用预加载功能,你需要在 Google Chrome 团队维护的网站 [HSTS 预加载列表提交](https://hstspreload.org)注册你的域。 -> My recommendation is to use `Strict-Transport-Security: max-age=31536000; includeSubDomains;` which instructs the browser to enforce a valid HTTPS connection to the origin host and to all subdomains for a year. If you are confident that your app can handle HTTPS-only, I would also recommend adding the `preload` directive, in which case don’t forget to register your website on the preload list as well, as noted above! +注意谨慎使用 `preload`,因为这意味着它不能轻易撤销,并带有几个月前的更新。虽然预加载肯定会加强应用程序的安全性,但也意味着你需要充分确信你的应用程序可以支持仅 HTTPS! -我推荐的用法是 `Strict-Transport-Security: max-age=31536000; includeSubDomains;`,这样指示了浏览器强制通过 HTTPS 连接到源主机并且有效期为一年。如果你对你的 app 处理 HTTPS 限定很有信心,我也推荐加上 `preload` 指令,当然别忘记去前面提到的预加载列表注册你的网站。 - -> Here’s what implementing HSTS looks like in Node.js: +我建议的用法是 `Strict-Transport-Security: max-age=31536000; includeSubDomains;`,这样指示了浏览器强制通过 HTTPS 连接到源主机并且有效期为一年。如果你对你的 app 处理 HTTPS 限定很有信心,我也推荐加上 `preload` 指令,当然别忘记去前面提到的预加载列表注册你的网站。 以下是在 Nodes.js 中实现 HSTS 的方法: @@ -145,13 +99,9 @@ function requestHandler(req, res){ ### 启用 XSS 过滤 ### -> In a reflected cross-site scripting attack (reflected XSS), an attacker injects malicious JavaScript code into an HTTP request, with the injected code “reflected” in the response and executed by the browser rendering the response, enabling the malicious code to operate within a trusted context, accessing potentially confidential information such as session cookies. Unfortunately, XSS is a pretty common web app attack, and a surprisingly effective one! - -在反射型跨站脚本攻击(reflected XSS)中,攻击者将恶意 JavaScript 代码注入到 HTTP 请求,注入的代码「映射」到响应中,并由浏览器执行,从而使恶意代码在可信任的上下文中执行,访问诸如会话 cookie 的潜在机密信息。不幸的是,XSS 是一个很常见的网络应用程序,且令人惊讶地有效! - ->To understand a reflected XSS attack, consider the Node.js code below, rendering mywebapp.com, a mock and intentionally simple web app that renders search results alongside the search term requested by the user: +在反射型跨站脚本攻击(reflected XSS)中,攻击者将恶意 JavaScript 代码注入到 HTTP 请求,注入的代码「映射」到响应中,并由浏览器执行,从而使恶意代码在可信任的上下文中执行,访问诸如会话 cookie 中的潜在机密信息。不幸的是,XSS 是一个很常见的网络应用攻击,且令人惊讶地有效! -为了了解反射型 XSS 攻击,参考以下 Node.js 代码,渲染 `mywebapp.com` 一个模拟并有意简单的 web 应用程序,它将搜索结果以及用户请求的搜索关键词一起呈现: +为了了解反射型 XSS 攻击,参考以下 Node.js 代码,渲染 `mywebapp.com`,模拟一个简单的 web 应用程序,它将搜索结果以及用户请求的搜索关键词一起呈现: ```javascript function handleRequest(req, res) { @@ -173,25 +123,19 @@ function handleRequest(req, res) { }; ``` ->Now, consider how will the web app above handle a URL constructed with malicious executable code embedded within the URL, such as this: - 现在,来考虑一下上面的 web 应用程序会如何处理在 URL 中嵌入的恶意可执行代码,例如: ``` https://mywebapp.com/search?

``` -> As you may realize, this URL will make the browser run the injected script and send the user’s cookies, potentially including confidential session cookies, to evil.com! - 你可能意识到了,这个 URL 会让浏览器执行注入的脚本,并发送用户的 cookies,极有可能包含机密的会话 cookie,至 evil.com。 -> To help protect users against reflective XSS attacks, some browsers have implemented protection mechanisms. These mechanisms try to identify these attacks by looking for matching code patterns in the HTTP request and response. Internet Explorer was the first browser to introduce such a mechanism with its XSS filter, introduced in Internet Explorer 8 back in 2008, and WebKit later introduced XSS Auditor, available today in Chrome and Safari. (Firefox has no similar mechanism built in, but users can use add-ons to gain this functionality.) These various protection mechanisms are not perfect: They may fail to detect a real XSS attack (a false negative), and in other cases may block legitimate code (a false positive). Due to the latter, browsers allow users to disable the XSS filter via the settings. Unfortunately, this is typically a global setting, which turns off this security feature completely for all web apps loaded by the browser. - -为了帮助保护用户抵抗反射型 XSS 攻击,有些浏览器实施了保护机制。这些保护机制尝试通过在 HTTP 请求和响应中寻找匹配的代码模式来辨识这些攻击。Internet Explorer 是第一个推出这种机制的,在 2008 年的 IE 8 中引入了 XSS 过滤器的机制,而 WebKit 后来推出了 XSS 审计,现今在 Chrome 和 Safari 上可用。(Firefox 没有内置类似的机制,但是用户可以使用插件来获得此功能)。这些保护机制并不完美,它们可能无法检测到真正的 XSS 攻击(漏报),在其他情况可能会阻止合法代码(误判)。由于后一种情况的出现,浏览器允许用户可设置禁用 XSS 过滤功能。不幸的是,这通常是一个全局设置,这会完全关闭所有浏览器加载的 web 应用程序的安全功能。 +为了保护用户抵抗反射型 XSS 攻击,有些浏览器实施了保护机制。这些保护机制尝试通过在 HTTP 请求和响应中寻找匹配的代码模式来辨识这些攻击。Internet Explorer 是第一个推出这种机制的,在 2008 年的 IE 8 中引入了 XSS 过滤器的机制,而 WebKit 后来推出了 XSS 审计,现今在 Chrome 和 Safari 上可用(Firefox 没有内置类似的机制,但是用户可以使用插件来获得此功能)。这些保护机制并不完美,它们可能无法检测到真正的 XSS 攻击(漏报),在其他情况可能会阻止合法代码(误判)。由于后一种情况的出现,浏览器允许用户可设置禁用 XSS 过滤功能。不幸的是,这通常是一个全局设置,这会完全关闭所有浏览器加载的 web 应用程序的安全功能。 > Luckily, there is a way for a web app to override this configuration and ensure that the XSS filter is turned on for the web app being loaded by the browser. This is done via the `X-XSS-Protection` header. This header, supported by Internet Explorer (from version 8), Edge, Chrome and Safari, instructs the browser to turn on or off the browser’s built-in protection mechanism and to override the browser’s local configuration. -幸运的是,有方法可以让 web 应用程序覆盖此配置,并确保浏览器加载的 web 应用已打开 XSS 过滤器。这是通过设定 `X-XSS-Protection` 响应头来达到的。此响应头支持 Internet Explorer (8以上)、Edge、Chrome 和 Safar,指示浏览器打开或关闭浏览器内置的保护机制,及覆盖浏览器的本地配置。 +幸运的是,有方法可以让 web 应用覆盖此配置,并确保浏览器加载的 web 应用已打开 XSS 过滤器。这是通过设定 `X-XSS-Protection` 响应头来达到的。此响应头支持 Internet Explorer (8以上)、Edge、Chrome 和 Safari,指示浏览器打开或关闭内置的保护机制,及覆盖浏览器的本地配置。 `X-XSS-Protection` 指令包括: @@ -200,13 +144,11 @@ https://mywebapp.com/search?

``` -你可能意识到了,这个 URL 会让浏览器执行注入的脚本,并发送用户的 cookies,极有可能包含机密的会话 cookie,至 evil.com。 +你可能意识到了,这个 URL 会让浏览器执行注入的脚本,并发送极有可能包含机密会话的用户 cookies 到 evil.com。 为了保护用户抵抗反射型 XSS 攻击,有些浏览器实施了保护机制。这些保护机制尝试通过在 HTTP 请求和响应中寻找匹配的代码模式来辨识这些攻击。Internet Explorer 是第一个推出这种机制的,在 2008 年的 IE 8 中引入了 XSS 过滤器的机制,而 WebKit 后来推出了 XSS 审计,现今在 Chrome 和 Safari 上可用(Firefox 没有内置类似的机制,但是用户可以使用插件来获得此功能)。这些保护机制并不完美,它们可能无法检测到真正的 XSS 攻击(漏报),在其他情况可能会阻止合法代码(误判)。由于后一种情况的出现,浏览器允许用户可设置禁用 XSS 过滤功能。不幸的是,这通常是一个全局设置,这会完全关闭所有浏览器加载的 web 应用程序的安全功能。 -> Luckily, there is a way for a web app to override this configuration and ensure that the XSS filter is turned on for the web app being loaded by the browser. This is done via the `X-XSS-Protection` header. This header, supported by Internet Explorer (from version 8), Edge, Chrome and Safari, instructs the browser to turn on or off the browser’s built-in protection mechanism and to override the browser’s local configuration. - -幸运的是,有方法可以让 web 应用覆盖此配置,并确保浏览器加载的 web 应用已打开 XSS 过滤器。这是通过设定 `X-XSS-Protection` 响应头来达到的。此响应头支持 Internet Explorer (8以上)、Edge、Chrome 和 Safari,指示浏览器打开或关闭内置的保护机制,及覆盖浏览器的本地配置。 +幸运的是,有方法可以让 web 应用覆盖此配置,并确保浏览器加载的 web 应用已打开 XSS 过滤器。即通过设定 `X-XSS-Protection` 响应头实现。此响应头支持 Internet Explorer (8以上)、Edge、Chrome 和 Safari,指示浏览器打开或关闭内置的保护机制,及覆盖浏览器的本地配置。 `X-XSS-Protection` 指令包括: - `1` 或者 `0` -使用或禁用 CSS 过滤器。 +使用或禁用 XSS 过滤器。 - `mode=block` 当检测到 XSS 攻击时,这会指示浏览器不渲染整个页面。 -> I recommend always turning on the XSS filter, as well as block mode, to maximize user protection. Such a response header looks like this: - 我建议永远打开 XSS 过滤器以及 block 模式,以求最大化保护用户。这样的响应头应该是这样的: ``` @@ -157,13 +153,13 @@ X-XSS-Protection: 1; mode=block 以下是在 Node.js 中配置此响应头的方法: ```javascript -functionrequestHandler(req, res){ +function requestHandler(req, res){ res.setHeader('X-XSS-Protection','1;mode=block');} ``` ### 控制 iframe ### -iframe (正式来说,是 HTML 内联框架元素)是一个 DOM 元素,它允许一个 web 应用嵌套在另一个 web 应用中。这个强大的元素有部分重要的使用场景,比如在 web 应用中嵌入第三方内容,但它也有重大的缺点,例如对 SEO 不友好,对浏览器导航跳转也不友好,还有很多。 +iframe (正式来说,是 HTML 内联框架元素)是一个 DOM 元素,它允许一个 web 应用嵌套在另一个 web 应用中。这个强大的元素有部分重要的使用场景,比如在 web 应用中嵌入第三方内容,但它也有重大的缺点,例如对 SEO 不友好,对浏览器导航跳转也不友好等等。 其中一个需要注意的事是它使得点击劫持变得更加容易。点击劫持是一种诱使用户点击并非他们想要点击的目标的攻击。要理解一个简单的劫持实现,参考以下 HTML,当用户认为他们点击可以获得奖品时,实际上是试图欺骗用户购买面包机。 @@ -176,11 +172,11 @@ iframe (正式来说,是 HTML 内联框架元素)是一个 DOM 元素, ``` -点击劫持有许多恶意应用程序,例如欺骗用户确认 Facebook 点赞,在线购买商品,甚至提交机密信息。恶意 web 应用程序可以通过在其恶意应用中嵌入合法的 web 应用来利用 iframe 进行点击劫持,这可以通过设置 `opacity: 0` 的 CSS 规则将其隐藏,并将 iframe 的点击目标直接放置在看起来无辜的按钮之上。点击了这个无辜按钮的用户会直接点击在嵌入的 web 应用上,并不知道点击后的作用。 +有许多恶意应用程序都采用了点击劫持,例如诱导用户点赞,在线购买商品,甚至提交机密信息。恶意 web 应用程序可以通过在其恶意应用中嵌入合法的 web 应用来利用 iframe 进行点击劫持,这可以通过设置 `opacity: 0` 的 CSS 规则将其隐藏,并将 iframe 的点击目标直接放置在看起来无辜的按钮之上。点击了这个无害按钮的用户会直接点击在嵌入的 web 应用上,并不知道点击后的后果。 -阻止这种攻击的一种有效的方法是限制你的 web 应用被框架化。`X-Frame-Options`,在 [RFC 7034](https://www.ietf.org/rfc/rfc7034.txt) 中引入,就是设计用来做这件事的。此响应头指示浏览器对你的 web 应用是否可以被嵌入另一个网页进行限制,从而阻止恶意网页欺骗用户调用你的应用程序进行各项操作。你可以使用 `DENY` 完全屏蔽,或者使用 `ALLOW-FROM` 指定将特定域列入白名单,也可以使用 `SAMEORIGIN` 指令将应用的源地址列入白名单。 +阻止这种攻击的一种有效的方法是限制你的 web 应用被框架化。在 [RFC 7034](https://www.ietf.org/rfc/rfc7034.txt) 中引入的 `X-Frame-Options`,就是设计用来做这件事的。此响应头指示浏览器对你的 web 应用是否可以被嵌入另一个网页进行限制,从而阻止恶意网页欺骗用户调用你的应用程序进行各项操作。你可以使用 `DENY` 完全屏蔽,或者使用 `ALLOW-FROM` 指令将特定域列入白名单,也可以使用 `SAMEORIGIN` 指令将应用的源地址列入白名单。 -我的建议是使用 `SAMEORIGIN` 指令,因为它允许 iframe 被用于同一域上的可以保证安全性的应用程序,这有时是有用的。以下是响应头的示例: +我的建议是使用 `SAMEORIGIN` 指令,因为它允许 iframe 被同域的应用程序所使用,这有时是有用的。以下是响应头的示例: ``` X-Frame-Options: SAMEORIGIN @@ -189,7 +185,7 @@ X-Frame-Options: SAMEORIGIN 以下是在 Node.js 中设置此响应头的示例代码: ```javascript -functionrequestHandler(req, res){ +function requestHandler(req, res){ res.setHeader('X-Frame-Options','SAMEORIGIN');} ``` @@ -197,31 +193,31 @@ functionrequestHandler(req, res){ 如前所述,你可以通过启用浏览器的 XSS 过滤器,给你的 web 应用程序增强安全性。然而请注意,这种机制是有局限性的,不是所有浏览器都支持(例如 Firefox 就不支持 XSS 过滤),并且依赖的模式匹配技术可以被欺骗。 -对抗 XSS 和其他攻击的更多一层的保护,可以通过明确列出可信来源和操作来实现 —— 这就是内容安全策略(CSP)。 +对抗 XSS 和其他攻击的另一层的保护,可以通过明确列出可信来源和操作来实现 —— 这就是内容安全策略(CSP)。 -CSP 是一种 W3C 规范,它定义了强大的基于浏览器的安全机制,可以对 web 应用中的资源加载以及脚本执行进行精细的控制。使用 CSP 可以将特定的域加入白名单进行例如脚本加载、AJAX 调用、图像加载和样式加载。你可以启用或禁用内联脚本或动态脚本(臭名昭著的 `eval`),并通过将特定域列入白名单来控制框架化。CSP 的另一个很酷的功能是它允许配置实时报告目标,以便实时监控应用程序进行 CSP 阻止操作。 +CSP 是一种 W3C 规范,它定义了强大的基于浏览器的安全机制,可以对 web 应用中的资源加载以及脚本执行进行精细的控制。使用 CSP 可以将特定的域加入白名单进行脚本加载、AJAX 调用、图像加载和样式加载等操作。你可以启用或禁用内联脚本或动态脚本(臭名昭著的 `eval`),并通过将特定域列入白名单来控制框架化。CSP 的另一个很酷的功能是它允许配置实时报告目标,以便实时监控应用程序进行 CSP 阻止操作。 这种对资源加载和脚本执行的明确的白名单提供了很强的安全性,在很多情况下都可以防范攻击。例如,使用 CSP 禁止内联脚本,你可以防范很多反射型 XSS 攻击,因为它们依赖于将内联脚本注入到 DOM。 CSP 是一个相对复杂的响应头,它有很多种指令,在这里我不详细展开了,可以参考 HTML5 Rocks 里一篇很棒的[教程](https://www.html5rocks.com/en/tutorials/security/content-security-policy/),其中提供了 CSP 的概述,我非常推荐阅读它来学习如何在你的 web 应用中使用 CSP。 -以下是一个设置 CSP 的示例代码,它仅允许从应用程序的源域加载脚本,并组织动态脚本的执行(eval)以及内嵌脚本(当然,还是 Node.js): +以下是一个设置 CSP 的示例代码,它仅允许从应用程序的源域加载脚本,并阻止动态脚本的执行(eval)以及内嵌脚本(当然,还是 Node.js): ```javascript -functionrequestHandler(req, res){ +function requestHandler(req, res){ res.setHeader('Content-Security-Policy',"script-src 'self'");} ``` ### 防止 Content-Type 嗅探 ### -为了使用户体验尽可能无缝,许多浏览器实现了一个功能叫内容类型嗅探,或者 MIME 嗅探。这个功能使得浏览器可以通过「嗅探」实际 HTTP 响应的资源的内容直接检测到资源的类型,无论在响应头中 `Content-Type` 是如何指定资源类型的。虽然这个功能在某些情况下确实是有用的,它引入了一个漏洞以及一种叫 MIME 混乱攻击的攻击手法。MIME 嗅探漏洞使攻击者可以注入恶意资源,例如恶意脚本,伪装成一个无辜的资源,例如一个图片。通过 MIME 嗅探,浏览器将忽略声明的图像内容类型,它不会渲染图片,而是执行恶意脚本。 +为了使用户体验尽可能无缝,许多浏览器实现了一个功能叫内容类型嗅探,或者 MIME 嗅探。这个功能使得浏览器可以通过「嗅探」实际 HTTP 响应的资源的内容直接检测到资源的类型,无视响应头中 `Content-Type` 指定的资源类型。虽然这个功能在某些情况下确实是有用的,它引入了一个漏洞以及一种叫 MIME 类型混淆攻击的攻击手法。MIME 嗅探漏洞使攻击者可以注入恶意资源,例如恶意脚本,伪装成一个无害的资源,例如一张图片。通过 MIME 嗅探,浏览器将忽略声明的图像内容类型,它不会渲染图片,而是执行恶意脚本。 幸运的是,`X-Content-Type-Options` 响应头缓解了这个漏洞。此响应头在 2008 年引入 IE8,目前大多数主流浏览器都支持(Safari 是唯一不支持的主流浏览器),它指示浏览器在处理获取的资源时不使用嗅探。因为 `X-Content-Type-Options` 仅在 [「Fetch」规范](https://fetch.spec.whatwg.org/#x-content-type-options-header)中正式指定,实际的实现因浏览器而异。一部分浏览器(IE 和 Edge)完全阻止了 MIME 嗅探,而其他一些(Firefox)仍然会进行 MIME 嗅探,但会屏蔽掉可执行的资源(JavaScript 和 CSS)如果声明的内容类型与实际的类型不一致。后者符合最新的 Fetch 规范。 `X-Content-Type-Options` 是一个很简单的响应头,它只有一个指令,`nosniff`。它是这样指定的:`X-Content-Type-Options: nosniff`。以下是示例代码: ```javascript -functionrequestHandler(req, res){ +function requestHandler(req, res){ res.setHeader('X-Content-Type-Options','nosniff');} ``` @@ -235,14 +231,16 @@ functionrequestHandler(req, res){ - 通过 `Strict-Transport-Security` 强制使用 HTTPS,并将你的域添加到 Chrome 预加载列表 - 利用 `X-XSS-Protection` 使你的 web 应用更加能抵抗 XSS 攻击 - 使用 `X-Frame-Options` 阻止点击劫持 -- 利用 `Content-Security-Policy` 将特定来源于端点列入白名单 +- 利用 `Content-Security-Policy` 将特定来源与端点列入白名单 - 使用 `X-Content-Type-Options` 防止 MIME 嗅探攻击 请记住,为了使 web 真正迷人,它必须是安全的。利用 HTTP 响应头构建更加安全的网页吧! -(**声明:** 此文内容仅属本人,不代表本人过去或现在的雇主。) +(**声明:** 此文内容仅属本人,不代表本人过去或现在的雇主。) + +(首页图片版权:[Pexels.com](https://www.pexels.com/photo/coffee-writing-computer-blogging-34600/)) --- From ae3e13d882eb36233da4bcedd7ff9eba72ec6c12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=B9=E5=8F=B7=E4=B8=89?= Date: Mon, 17 Apr 2017 22:45:12 +0800 Subject: [PATCH 189/638] Create testing-views-in-isolation-with-espresso.md --- ...esting-views-in-isolation-with-espresso.md | 170 ++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 TODO/testing-views-in-isolation-with-espresso.md diff --git a/TODO/testing-views-in-isolation-with-espresso.md b/TODO/testing-views-in-isolation-with-espresso.md new file mode 100644 index 00000000000..c746cbfcf68 --- /dev/null +++ b/TODO/testing-views-in-isolation-with-espresso.md @@ -0,0 +1,170 @@ +> * 原文地址:[Testing Views in Isolation with Espresso](https://www.novoda.com/blog/testing-views-in-isolation-with-espresso/) +> * 原文作者:[Ataul Munim](https://www.novoda.com/blog/author/ataulm/) +> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) +> * 译者: +> * 校对者: + +# Testing Views in Isolation with Espresso # + + +In this post, we'll show you how and why you should use Espresso to test your custom Views on an Android device. + +You can use Espresso to test entire screens or flows at once. These tests launch an Activity and perform actions as a user would, including waiting for data to load or navigating to other screens. + +They're useful because you need end-to-end tests to validate common user flows. These automated tests should run at regular intervals, enabling you to spend manual QA time performing exploratory tests. + +That said, these aren’t tests that you can run frequently. Running the whole suite can take hours (imagine a suite that includes verifying offline sync for media content), so you might choose to run them at night. + +It's difficult because these types of tests incorporate multiple points of potential failure. Ideally when a single test fails, you want it to fail because of a single logical assertion. + +Most (or a lot) of the regressions you can introduce are in the UI. They might be subtle so we don't notice them when adding new features, but eagle-eyed QA teams often do. + +It's such a waste of time. + +## What can you do? ## + +Let's look at using Espresso to test that you have correctly bound data to Views. + +At Novoda, the Views we write are mostly extensions of existing View and ViewGroup classes on Android. They typically only expose one or two extra methods, which are used to bind callbacks and the data object/view model, like this: + +``` +public class MovieItemView extends RelativeLayout { + private TextView titleTextView; + private Callback callback; + + public void attach(Callback callback) { + this.callback = callback; + } + + public void bind(Movie movie) { + titleTextView.setText(movie.name()); + setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + callback.onClick(movie); + } + }); + } +} +``` + +They group logical parts of the UI together and often encompass naming conventions from the business domain. You will rarely see ‘raw’ Android Views in Activity layouts in our work at Novoda. + +Let’s write these View tests in a BDD style, like "given MovieItemView is bound to Edward Scissorhands, then title is set as Edward Scissorhands" or "given MovieItemView is bound to Edward Scissorhands, when clicking on view, then onClick(Edward Scissorhands) is called", etc. + +## Couldn't you have caught these regressions with unit tests? ## + +Why do you need Espresso to run these tests if you’re using a presentation pattern like MVP or MVVM, which can be unit-tested? + +First, let’s go over the presentation flow and describe what you can test to see how the Espresso tests can augment it. + +- Presenters subscribe to data producers that send events +- Events can be of type `loading`, `idle` or `error`, and may or may not contain data to display +- Presenters will forward these events to "displayers" (“View” in MVP) using methods like `display(List)`, `displayCachedDataWhileLoading(List)` or `displayEmptyScreen()`, etc. +- The implementation of displayers will show/hide Android Views and do things like `moviesView.bind(List)` + +You can unit-test the presenters completely, verifying that the correct methods on the displayers are called with the correct arguments. + +Can you test the displayers in the same way? Yes, you can mock the Android Views and verify you're calling the correct methods on them. It won't be at the correct granularity though: + +- your displayer could create or update the adapter of a RecyclerView or ViewPager, but this gives you no assurances of what the item/page is displaying +- Android Views are setup in code with attributes inflated from XML (layouts and styles); verifying method calls isn't enough to assert what's displayed + +## Setting up for the tests ## + +Let’s use the [`espresso-support`](https://github.com/novoda/spikes/tree/master/espresso-support) library to get started. + +Add the dependencies to your build.gradle file (available from JCenter): + +``` +debugCompile 'com.novoda:espresso-support-extras:0.0.3' +androidTestCompile 'com.novoda:espresso-support:0.0.3' +``` + +The `extras` artifact includes the `ViewActivity`, which needs to be part of your app under test. You can use this Activity to hold a single View, which you can test with Espresso. + +The core artifact (containing custom test rules) only needs to be included as part of your `androidTest` dependencies. + +The `ViewTestRule` is used in a similar way to the `ActivityTestRule`. Instead of passing the Activity class that you want to launch, you should pass the layout file containing the View you want to test: + +``` +@RunWith(AndroidJUnit4.class)publicclassMovieItemViewTest{ + @Rule + public ViewTestRule viewTestRule=newViewTestRule<>(R.layout.test_movie_item_view); + ... +``` + +You can specify the View type for the root of the layout with `ViewTestRule`. + +The `ViewTestRule` extends `ActivityTestRule`, so it'll always open `ViewActivity`. `getActivityIntent()` is overridden so you can pass `R.layout.test_movie_item_view` to `ViewActivity` as an Intent extra. + +You can use Mockito in your tests to substitute for the callbacks: + +``` +@Rule +public MockitoRule mockitoRule = MockitoJUnit.rule(); + +@Mock +MovieItemView.Listener movieItemListener; + +@Before +publicvoidsetUp(){ + MovieItemView view = viewTestRule.getView(); + view.attachListener(movieItemListener); + ... + } +``` + +ViewTestRule has a method `bindViewUsing(Binder)`, which gives you a reference to the View so you can interact with it. While you could access the View directly with `viewTestRule.getView()`, you want to ensure any interaction with the View is performed on the main thread, not the test thread. + +``` +@Before +public void setUp() { + MovieItemView view = viewTestRule.getView(); + view.attachListener(movieItemListener); + viewTestRule.bindViewUsing(new ViewTestRule.Binder() { + @Override + public void bind(MovieItemView view) { + view.bind(EDWARD_SCISSORHANDS); + } + }); +} +``` + +## Ready to test ## + +Apps only really do two things, from the user's point of view: + +- they display information +- they respond to user actions + +To write tests for these two cases, you can start by asserting that the correct information is displayed using standard Espresso ViewMatchers and ViewAssertions: + +``` +@Test +public void titleSetToMovieName() { + onView(withId(R.id.movie_item_text_name)) + .check(matches(withText(EDWARD_SCISSORHANDS.name))); +} +``` + +Next, you should make sure that the user actions correspond to the correct event being fired, with the correct arguments: + +``` +@Test +public void clickMovieItemView() { + onView(withClassName(is(MovieItemView.class.getName()))) + .perform(click()); + + verify(movieItemListener) + .onClick(eq(EDWARD_SCISSORHANDS)); +} +``` + +That's it, I hope you find this useful. + +In a future post, I'll cover using Espresso to test your Views for TalkBack support. + +--- + +> [掘金翻译计划](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)、[React](https://github.com/xitu/gold-miner#react)、[前端](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)。 From 0a04324fc5ff3a0e4ab4691953d980144358ff9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=B9=E5=8F=B7=E4=B8=89?= Date: Mon, 17 Apr 2017 23:00:36 +0800 Subject: [PATCH 190/638] Create es6-modules-support-lands-in-browsers-is-it-time-to-rethink-bundling.md --- ...browsers-is-it-time-to-rethink-bundling.md | 335 ++++++++++++++++++ 1 file changed, 335 insertions(+) create mode 100644 TODO/es6-modules-support-lands-in-browsers-is-it-time-to-rethink-bundling.md diff --git a/TODO/es6-modules-support-lands-in-browsers-is-it-time-to-rethink-bundling.md b/TODO/es6-modules-support-lands-in-browsers-is-it-time-to-rethink-bundling.md new file mode 100644 index 00000000000..2481f7b2bab --- /dev/null +++ b/TODO/es6-modules-support-lands-in-browsers-is-it-time-to-rethink-bundling.md @@ -0,0 +1,335 @@ +> * 原文地址:[ES6 modules support lands in browsers: is it time to rethink bundling?](https://www.contentful.com/blog/2017/04/04/es6-modules-support-lands-in-browsers-is-it-time-to-rethink-bundling/) +> * 原文作者:[Stefan Judis](https://www.contentful.com/about-us/) +> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) +> * 译者: +> * 校对者: + +# ES6 modules support lands in browsers: is it time to rethink bundling? # + +![](http://images.contentful.com/256tjdsmm689/3xFvPzCb6wUek00gQAuU6q/0e8221e0e5c673f18d20448a9ba8924a/Contentful_ES6Modules_.png) + +Writing performant JavaScript applications is a complex matter these days. Years ago, everything started with script concatenation to save HTTP requests, and then it continued with minification and wrangling of variable names to squeeze out even the last tiny bit of the code we ship. + +Today we have [tree shaking](https://blog.engineyard.com/2016/tree-shaking) and module bundlers, and we go back to code splitting to not block the main thread on startup and speed up [the time to interactivity](https://developers.google.com/web/tools/lighthouse/audits/time-to-interactive). We're also transpiling everything: using future features today? No problem – thanks to Babel! + +ES6 modules have been defined in the ECMAScript specification [for a while already](http://2ality.com/2014/09/es6-modules-final.html). The community wrote tons of articles on how to use them with Babel and how `import` differs from `require` in Node.js, but it took a while until an actual implementation landed in browsers. I was surprised to see that Safari was the first one shipping ES6 modules in its technology preview channel, and now Edge and Firefox Nightly also ship this feature – even though it's behind a flag. After having used tools like [RequireJS](http://requirejs.org/) and [Browserify](http://browserify.org/) (remember [the AMD and CommonJS discussions](https://addyosmani.com/writing-modular-js/)?) it looks like modules are finally arriving in the browser landscape, so let's see a look what the bright future will bring. 🎉 + + +## The traditional setup ## + +The usual way to build web applications is to include one single bundle that is produced using Browserify, Rollup or Webpack (or any other tool out there). A classic website that's not a SPA (single page application) consists of server-side generated HTML, which then includes a single JavaScript bundle. + +``` + + + ES6 modules tryout + + + + + + + +``` + +The combined file includes three JavaScript files bundled with Webpack. These files make use of ES6 modules: + +``` +// app/index.js +import dep1 from './dep-1'; + +function getComponent () { + var element = document.createElement('div'); + element.innerHTML = dep1(); + return element; +} + +document.body.appendChild(getComponent()); + +// app/dep-1.js +import dep2 from './dep-2'; + +export default function() { + return dep2(); +} + +// app/dep-2.js +export default function() { + return 'Hello World, dependencies loaded!'; +} +``` + +The result of this app will be a "Hello world" telling us that all files are loaded. + +### Shipping a bundle ### + +The Webpack configuration to create this bundle is relatively straightforward. There is not much happening right now except for the bundling and minification of the JavaScript files using UglifyJS. + +``` +// webpack.config.js + +const path = require('path'); +const UglifyJSPlugin = require('uglifyjs-webpack-plugin'); + +module.exports = { + entry: './app/index.js', + output: { + filename: 'bundle.js', + path: path.resolve(__dirname, 'dist') + }, + plugins: [ + new UglifyJSPlugin() + ] +}; +``` + +The three base files are relatively small and have a total size of 347 bytes. + +``` +$ ll app +total 24 +-rw-r--r-- 1 stefanjudis staff 75B Mar 16 19:33 dep-1.js +-rw-r--r-- 1 stefanjudis staff 75B Mar 7 21:56 dep-2.js +-rw-r--r-- 1 stefanjudis staff 197B Mar 16 19:33 index.js +``` + +When I ran this through Webpack, I got a bundle with the size of 856 bytes, which is roughly 500 bytes boilerplate. These additional bytes are acceptable, as it's nothing compared to the bundles most of us ship in production. Thanks to Webpack, we can already use ES6 modules. + + +``` +$ webpack +Hash: 4a237b1d69f142c78884 +Version: webpack 2.2.1 +Time: 114ms +Asset Size Chunks Chunk Names +bundle.js 856 bytes 0 [emitted] main + [0] ./app/dep-1.js 78 bytes {0}[built] + [1] ./app/dep-2.js 75 bytes {0}[built] + [2] ./app/index.js 202 bytes {0}[built] +``` + +## The new setup using native supported ES6 modules ## + +Now that we have the "traditional bundle" for all the browsers that don't support ES6 modules yet, we can start playing around with the cool stuff. To do so, let's add in the `index.html` file a new script element pointing to the ES6 module with `type="module"`. + + +``` +ES6 modules tryout +``` + +When we take a look at Chrome, we'll see that there is not much more happening. + +![image01](http://images.contentful.com/256tjdsmm689/4JHwnbyrssomECAG2GI8se/e8e35adc37bc0627f0902bcc2fdb52df/image01.png) + +The bundle is loaded as before, "Hello world!" is shown, but that's it. And that's excellent, because this is how web works: browsers are forgiving, they won't throw errors when they don't understand markup we send down the wire. Chrome just ignores the script element with the type it doesn't know. + +Now, let's check the Safari technology preview: + +![Bildschirmfoto 2017-03-29 um 17.06.26](http://images.contentful.com/256tjdsmm689/1mefe0J3JKOiAoSguwMkka/0d76c5666300ed0b631a0fe548ac5b52/Bildschirmfoto_2017-03-29_um_17.06.26.png) + +Sadly, there is no additional "Hello world" showing up. The reason is the difference between build tools and native ES modules: whereas Webpack figures out which files to include during the build process, when running ES modules in the browser, we need to define concrete file paths. + +``` +// app/index.js + +// This needs to be changed +// import dep1 from './dep-1'; + +// This works +import dep1 from './dep-1.js'; +``` + +The adjusted file paths work great, except for the fact that Safari preview now loads the bundle and the three individual modules, meaning that our code will be executed twice. + +![image02](http://images.contentful.com/256tjdsmm689/6MeIDF7GuW6gy8om4Ceccc/a0dba00a4e0f301f2a7fd65449d044ab/image02.png) + +The solution is the `nomodule` attribute, which we can set on the script element requesting the bundle. This attribute [was added to the spec quite recently](https://github.com/whatwg/html/commit/a828019152213ae72b0ed2ba8e35b1c472091817) and Safari Preview supports it as of the [end of January](https://trac.webkit.org/changeset/211078/webkit). It tells Safari that this script is the "fallback" script for the lack of ES6 modules support, and in this case shouldn't be executed. + +``` + + + ES6 modules tryout + + + + + + + + + +``` + +![image03](http://images.contentful.com/256tjdsmm689/1YchZEromA2ueKUCoYqMsc/2c68c46ffd2a3ad73d99d17020d56093/image03.png) + +That's good. With the combination of `type="module"` and `nomodule`, we can load a classic bundle in not supporting browsers and load JavaScript modules in supporting browsers. + +You can check out this state in production at [es-module-on.stefans-playground.rocks](http://es-module-on.stefans-playground.rocks/). + +### Differences between modules and scripts ### + +There are a few gotchas here, though. First of all, JavaScript running in an ES6 module is not quite the same as in a regular script element. Axel Rauschmayer covers this quite nicely in [his book Exploring ES6](http://exploringjs.com/es6/ch_modules.html#sec_modules-vs-scripts). I highly recommend you check it out but let's just quickly mention the main differences: + +- ES6 modules are running in strict mode by default (no need for `'use strict'` anymore). +- Top-level value of `this` is `undefined`. +- Top-level variables are local to the module. +- ES6 modules are loaded and executed asynchronously after the browser finished parsing the HTML. + +In my opinion, these are all huge advantages. Modules are local – there is no need for IIFEs around everything, and also we don't have to fear global variable leaking anymore. Also running in strict mode by default means that we can drop a lot of `'use strict'` statements. + +And from a performance point of view (probably the most important one) **modules load and execute deferred by default**. So we won't accidentally add blocking scripts to our website and there is no [SPOF](https://www.stevesouders.com/blog/2010/06/01/frontend-spof/) issue when dealing with script `type="module"` elements. We could place an `async` attribute on it, which overwrites the default deferred behavior, but `defer`[is a good choice these days](https://calendar.perfplanet.com/2016/prefer-defer-over-async/). + +``` + + + + + + + + +``` + +In case you want to check the details around that, the [script element spec](https://html.spec.whatwg.org/multipage/scripting.html#the-script-element) is an understandable read and includes some examples. + +## Minifying of pure ES6 ## + +But we're not quite there yet! We serve a minified bundle for Chrome and individual not minified files for Safari Preview now. How can we make these smaller? UglifyJS should do the job just fine, right? + +It turns out that UglifyJS is not able to fully deal with ES6 code yet. There is a `harmony` development branch available, but unfortunately it didn't work with my three JavaScript files at the time of writing. + +``` +$ uglifyjs dep-1.js -o dep-1.min.js +Parse error at dep-1.js:3,23 +export default function() { + ^ +SyntaxError: Unexpected token: punc (() +// .. +FAIL: 1 +``` + + +But UglifyJS is in every toolchain today, how does this work for all the projects written in ES6 out there? + +The usual flow is that tools like Babel transpile to ES5, and then Uglify comes into play to minify this ES5 code. I want to ignore ES5 transpilation in this article: we're dealing with the future here, Chrome has [97% ES6 coverage](https://kangax.github.io/compat-table/es6/#chrome59) and Safari Preview has already fabulous [100% ES6 coverage since version 10](https://kangax.github.io/compat-table/es6/#safari10_1). + +I asked the Twittersphere if there is a minifier available that can deal with ES6, and [Lars Graubner](https://twitter.com/larsgraubner) pointed me towards [Babili](https://github.com/babel/babili). Using Babili, we can easily minify the ES6 modules. + + +``` +// app/dep-2.js + +export default function() { + return 'Hello World. dependencies loaded.'; +} + +// dist/modules/dep-2.js +export default function(){return 'Hello World. dependencies loaded.'} +``` + +With the Babili CLI tool, it's almost too easy to minify all the files separately. + +``` +$ babili app -d dist/modules +app/dep-1.js -> dist/modules/dep-1.js +app/dep-2.js -> dist/modules/dep-2.js +app/index.js -> dist/modules/index.js +``` + +The result looks then as follows. + +``` +$ ll dist +-rw-r--r-- 1 stefanjudis staff 856B Mar 16 22:32 bundle.js + +$ ll dist/modules +-rw-r--r-- 1 stefanjudis staff 69B Mar 16 22:32 dep-1.js +-rw-r--r-- 1 stefanjudis staff 68B Mar 16 22:32 dep-2.js +-rw-r--r-- 1 stefanjudis staff 161B Mar 16 22:32 index.js +``` + +The bundle is still roughly around 850B, and all the files are around 300B in total. I'm ignoring GZIP compression here as [it doesn't work well on such small file sizes](http://webmasters.stackexchange.com/questions/31750/what-is-recommended-minimum-object-size-for-gzip-performance-benefits) (we'll get back to that later). + +## Speeding up ES6 modules with rel=preload? ## + +The minification of the single JS files is a huge success. It's 298B vs. 856B, but we could even go further and speed things up more. Using ES6 modules we are now able to ship less code, but looking at the waterfall again we'll see that the requests are made sequentially because of the defined dependency chain of the modules. + +What if we could throw `` elements in the mix which can be used to tell the browser upfront that additionally requests will be made soon? We have build tool plugins like Addy Osmani's [Webpack preload plugin](https://github.com/GoogleChrome/preload-webpack-plugin) for code splitting already – is something like this possible for ES6 modules? In case you don't know how `rel="preload"` works, you should check out the [article on this topic](https://www.smashingmagazine.com/2016/02/preload-what-is-it-good-for/) by Yoav Weiss on Smashing Magazine. + +Unfortunately, preloading of ES6 modules is not so easy because they behave differently than normal scripts. The question is how a link element with a set `rel="preload"` attribute should treat an ES6 module? Should it fetch all the dependent files, too? This is an obvious question to answer, but there are more browser internal problems to solve, too, if module treatment should go into the `preload` directive. In case you're interested in this topic [Domenic Denicola](https://twitter.com/domenic) discusses these problems [in a GitHub issue](https://github.com/whatwg/fetch/issues/486), but it turns out that there are too many differences between scripts and modules to implement ES6 module treatment in the `rel="preload"` directive. The solution might be another `rel="modulepreload"` directive to clearly separate functionalities, with [the spec pull request](https://github.com/whatwg/html/pull/2383) pending at the time of writing, so let's see how we'll preload modules in the future. + +## Bringing in real dependencies ## + +Three files don't make a real app, so let's add a real dependency. Fortunately, [Lodash](https://lodash.com/) offers all of its functionality also in split ES6 modules, which I then minified using Babili. So let's modify the `index.js` file to also include a Lodash method. + + +``` +import dep1 from './dep-1.js'; +import isEmpty from './lodash/isEmpty.js'; + +function getComponent() { + const element = document.createElement('div'); + element.innerHTML = dep1() + ' ' + isEmpty([]); + + return element; +} + +document.body.appendChild(getComponent()); +``` + +The use of `isEmpty` is trivial in this case, but let's see what happens now after adding this dependency. + +![image07](http://images.contentful.com/256tjdsmm689/13F95Xpl32Mu0MgE0mgS2o/c9dbc002e53bf56ee0eeb0df40b55f9c/image07.png) + +The request count went up to over 40, the page load time went up from roughly 100ms to something between 400ms and 800ms on a decent wifi connection, and the shipped overall size increased to approximately 12KB without compression. Unfortunately, Safari Preview is not available on [WebPagetest](https://www.webpagetest.org/) to run some reliable benchmarks. + +Chrome receiving the bundled JavaScript, on the other hand, is at a slim ~8KB file size. + +![image05](http://images.contentful.com/256tjdsmm689/6xxfWBW9nqAeqQ8ck0MqU/62a74102e9247d785a61a84766356f51/image05.png) + +This 4KB difference is definitely something to check. You can find this example at [lodash-module-on.stefans-playground.rocks](https://lodash-module-on.stefans-playground.rocks/). + +### Compression works only well on larger files ### + +In case you looked closely at the screenshots of the Safari developer tools, you might have noticed that the transferred file size was actually bigger than the source. Especially in a large JavaScript app, including a lot of small chunks makes a big difference and that's because GZIP doesn't play well with small file sizes. + +Khan Academy [discovered the same thing](http://engineering.khanacademy.org/posts/js-packaging-http2.htm) a while ago when experimenting with HTTP/2. The idea of shipping smaller files is great to guarantee perfect cache hit ratios, but at the end, it's always a tradeoff and it's depending on several factors. For a large code base splitting the code into several chunks (a *vendor* and an *app* bundle) makes sense, but shipping thousands of tiny files that can't be compressed properly is not the right approach. + +### Tree shaking is the cool kid in town ### + +Another thing to point out is that thanks to the relatively new tree shaking mechanism, build processes can eliminate code that's not used and imported by any other module. The first build tool that supported this was Rollup, but now Webpack in version 2 supports it as well — [as long as we disable the `module` option in babel](https://medium.freecodecamp.com/tree-shaking-es6-modules-in-webpack-2-1add6672f31b#22c4). + +Let's say we changed `dep-2.js` to include things that won't be imported by `dep-1.js`. + +``` +export default function() { + return 'Hello World. dependencies loaded.'; +} + +export const unneededStuff = [ + 'unneeded stuff' +]; +``` + +Babili will simply minify the file and Safari Preview, in this case, would receive several code lines that are not used. A Webpack or Rollup bundle, on the other hand, won't include `unneededStuff`. Tree shaking offers huge savings that definitely should be used in a real production code base. + +## The future looks bright, but build processes are here to stay ## + +So, ES6 modules are on their way, but it doesn't look like anything will change when they finally arrive in all the major browsers. We won't start shipping thousands of tiny files to guarantee good compression, and we won't abandon build processes to make use of tree shaking and dead code elimination. **Frontend development is and will be as complicated as always**. + +**The most important thing to remember is that measuring is the key to succees**. Don't split everything and assume that it will lead to an improvement. Just because we might have support for ES6 modules in browsers soon, it doesn't mean that we can get rid of a build process and a proper "bundle strategy". Here at Contentful we'll stick to our build processes, and continue to ship bundles including our [JavaScript SDKs](https://www.contentful.com/developers/docs/javascript/). + +Yet, I have to admit that Frontend development still feels great. JavaScript evolves, and we'll finally have a way to deal with modules baked into the language. I can't wait to see how and if this influences the JavaScript ecosystem and what the best practices will be in a couple of years. + +## Additional resources ## + +- [Article series on ES6 modules](https://blog.hospodarets.com/native-ecmascript-modules-the-first-overview) by Serg Hospodarets +- [The modules chapter](http://exploringjs.com/es6/ch_modules.html) in "[Exploring ES6](http://exploringjs.com/)" + +--- + +> [掘金翻译计划](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)、[React](https://github.com/xitu/gold-miner#react)、[前端](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)。 From 2161ad5a7ef2005ccb44401457ae4ea8adbf02be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=B9=E5=8F=B7=E4=B8=89?= Date: Mon, 17 Apr 2017 23:12:26 +0800 Subject: [PATCH 191/638] Create everyone-is-a-designer-get-over-it.md --- TODO/everyone-is-a-designer-get-over-it.md | 85 ++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 TODO/everyone-is-a-designer-get-over-it.md diff --git a/TODO/everyone-is-a-designer-get-over-it.md b/TODO/everyone-is-a-designer-get-over-it.md new file mode 100644 index 00000000000..aee8b2663cd --- /dev/null +++ b/TODO/everyone-is-a-designer-get-over-it.md @@ -0,0 +1,85 @@ +> * 原文地址:[Everyone is a designer. Get over it.](https://library.gv.com/everyone-is-a-designer-get-over-it-501cc9a2f434) +> * 原文作者:[Daniel Burka](https://library.gv.com/@dburka) +> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) +> * 译者: +> * 校对者: + + +![](https://cdn-images-1.medium.com/max/2000/1*xIoFsnWI_2-1VOy00a2KrQ.jpeg) + +Photo by Alice Achterhof [via Unsplash](https://unsplash.com/search/designer-paint?photo=FwF_fKj5tBo) + +# Everyone is a designer. Get over it. # + +Recently, [Jared Spool](https://www.uie.com/about/) caught my attention with [an article](https://articles.uie.com/signup/) about how Netflix’s performance engineers are *actually designers*. It’s a provocative idea, but it makes sense. His argument is that everyone in your organization (including performance engineers) designs the product, not just the people with “design” in their job titles. + +![](https://cdn-images-1.medium.com/max/800/1*qLoczEHONP188zelJbn-6w@2x.png) + +From some of the reactions, you might think Jared had kidnapped someone’s baby for ritual sacrifice. What exactly did Jared write? + +> The members of this team are performance engineers. They are architecting, engineering, and maintaining the performance of a very complex system. It occupies all their time and then some. In systems engineering, there are few jobs more technical than these. + +> And yet, at the very moment that a Netflix viewer’s video stream stops and that spinning animation appears, indicating the player is now awaiting more data, these engineers make a dramatic change. **They become user experience designers.** + +I made that last sentence bold — because it’s really important. Some designers are uncomfortable with the idea that an engineer or a salesperson or a CFO could be a *designer*. + +![](https://cdn-images-1.medium.com/max/800/1*ErZDaGRy3mJ19jGdWqeJgA@2x.png) + +Common reactions + +Whether you like it or not, whether you approve it or not, people outside of your design team are making significant *design* choices that affect your customers in important ways. They are *designing* your product. They are *designers*. + +This shouldn’t be provocative — it’s just a statement of fact. I work with[ dozens of startups](http://www.gv.com/portfolio/) every year, and I see it happen at every one. A CFO makes a pricing decision and changes the product experience. An engineer makes a performance trade-off. A salesperson writes a script for talking to customers. In my view, people who fundamentally change the customer’s experience are *designers*. + +If this is so self-evident, why do Jared and I press the point? I keep beating the drum because I want designers to change the way they think about their role and become better stewards of good design. + +For a moment, consider how this shift in perspective could change the way you work. + +#### Everyone needs a design mindset #### + +When you accept the reality that design decisions are coming from outside your group, by people without “design” in their job titles, you approach your co-workers differently . Now they’re not just your co-workers — they’re your design team. + +The companies that produce great design, such as Apple and Airbnb, have learned this. Alex Schleifer, VP of design at Airbnb,[ tells *Wired*](https://www.wired.com/2015/01/airbnbs-new-head-design-believes-design-led-companies-dont-work/) how the company *isn’t* design-led: + +> *The solution [at Airbnb] actually deemphasizes the designers. The point… isn’t to create a “design-led culture,” because that tends to tell anyone who isn’t a designer that their insights take a backseat. It puts the entire organization in the position of having to react to one privileged point of view. Instead, Schleifer wants more people to appreciate what typically lies only within the realm of designers — the user viewpoint.* + +Does everyone need all the skills of a designer? Of course not. But each person needs to be armed with the tools to understand how their decisions affect the customer experience. + +When an engineer takes a shortcut and scrimps on performance, they need to understand how that damages the user experience. Likewise, when a designer pushes an engineer to make a change that affects performance, that engineer should help the designer make the best overall design decision — not just roll over and do what the designer asked. It’s this type of respectful collaboration that makes great design happen. + +One of the best ways to encourage empathy is to watch customer research studies with co-workers from across your company. When my colleague Michael Margolis runs a study with a GV company, we insist that the real team — not just the designers — watch those interviews and take notes. If it’s not possible for everyone to watch in real time, you can record the sessions and schedule a “viewing party” for later. + +#### Work outside your design team #### + +When you accept that design happens almost everywhere in your organization, you have to take responsibility for it. Your app is slow? Go sit with your engineering team. Your marketing team is poorly communicating your product to future customers? You’d better offer to work with them on the problem. + +Yes, doing design with everyone at your company is a lot of work. But it’s necessary if you want to be a truly great designer — otherwise, you’re simply papering over bad decisions. For example, imagine that your CEO created a complex pricing structure for your product. You could focus on making the pricing page as clear as possible using your interface and information design skills. But the harder and more important design opportunity is to work with your CEO on repricing your product so it’s clear to customers and compatible with the business goals. + +Focusing on the core business is what differentiates real product design from interface design or even user experience design. Fundamental product design is really hard and requires a lot of legwork, but this is what designers at the highest level do — and it’s why their work is better than yours. + +![](https://cdn-images-1.medium.com/max/600/1*czW-2nrN_3l50ZzgYQYqlw@2x.png) + +“The Disciplines of UX Design” by Dan Saffer and Thomas Gläser + +#### Grow your design team to include non-designers #### + +Design is a hard job. You’ll need a wide range of skills (look at all of those circles in the[ diagram of UX Disciplines](https://www.fastcodesign.com/1671735/infographic-the-intricate-anatomy-of-ux-design) by Dan Saffer) and years of practice to truly master design. + +Maybe that’s why so many designers are offended when non-designers do design work or get called “designers” by Jared and me. You can act offended if you want, but the reality is that other people are making design decisions with or without you. Embrace them . They don’t make your job less valuable. They don’t make your job title less meaningful. + +Having more people who *do design* is additive, not competitive. These designers make your team and your product stronger, because they’re contributing from their unique perspectives. Help them bolster their skills, and use their expertise to the advantage of your product and company. + +### **Let’s make a better future together** ### + +A few years ago, I met an executive from a Fortune 500 company. When I told her I’m a designer, her eyes lit up. “Oh, I love design!” she said. “My group is just down the hall from the design team. They do so much creative work in there.” + +My heart sank. The design team was just down the hall from this executive, but they didn’t work together. Instead, they sat behind soundproof glass walls and did special “creative work” in isolation. + +This executive made decisions every day that affected her customers. She bears some of the blame for not reaching out to the designers “in there.” But the design team is primarily at fault — for missing an opportunity to reach out and work together on some of the business’s most important challenges. + + +Thanks to [Jared M. Spool](https://medium.com/@jmspool) for writing an excellent article, [John Zeratsky](https://medium.com/@jazer) and [Michael Margolis](https://medium.com/@mmargolis) for editing and suggestions, and [Alex Schleifer](https://medium.com/@alexoid) for the excellent *Wired* article. + +--- + +> [掘金翻译计划](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)、[React](https://github.com/xitu/gold-miner#react)、[前端](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)。 From da5a893d854e280b50844c21b6a86057e9b21e3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=B9=E5=8F=B7=E4=B8=89?= Date: Mon, 17 Apr 2017 23:41:34 +0800 Subject: [PATCH 192/638] Create test-driving-away-coupling-in-activities.md --- ...est-driving-away-coupling-in-activities.md | 199 ++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 TODO/test-driving-away-coupling-in-activities.md diff --git a/TODO/test-driving-away-coupling-in-activities.md b/TODO/test-driving-away-coupling-in-activities.md new file mode 100644 index 00000000000..346204681d7 --- /dev/null +++ b/TODO/test-driving-away-coupling-in-activities.md @@ -0,0 +1,199 @@ +> * 原文地址:[Test Driving away Coupling in Activities](https://www.philosophicalhacker.com/post/test-driving-away-coupling-in-activities/) +> * 原文作者:[philosohacker](https://twitter.com/philosohacker) +> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) +> * 译者: +> * 校对者: + +# Test Driving away Coupling in Activities # + +`Activity`s and `Fragment`s, perhaps by [some strange historical accidents](/post/why-android-testing-is-so-hard-historical-edition/), have been seen as *the optimal* building blocks upon which we can build our Android applications for much of the time that Android has been around. Let’s call this idea – the idea that `Activity`s and `Fragment`s are the best building blocks for our apps – “android-centric” architecture. + +This series of posts is about the connection between the testability of android-centric architecture and the other problems that are now leading Android developers to reject it; it’s about how our unit tests are trying to tell us that `Activity`s and `Fragment`s – like the cracking bricks in the above image – don’t make the best building blocks for our apps because they tempt us to write code with *tight coupling* and *low cohesion*. + + +[Last time](/post/what-unit-tests-are-trying-to-tell-us-about-activities-pt-2/), we saw `Activity`s and `Fragment`s tend to have low cohesion. This time, we’ll see how our tests can tell us that code within `Activity`s have tight coupling. We’ll also see how test driving the functionality leads to a design that has looser coupling, which makes it easier to change the app and also opens up opportunities for removing duplication. As with the the other posts in the series, we’ll be discussing all of this using the Google I/O app as an example. + +### The Target Code ### + +The code that we want to test, the “target code”, does the following: when the user navigates to the map view that shows where all the Google I/O sessions are, it asks for their location. If they reject the permission, we show a toast notifying the user that they’ve disabled an app permission. Here’s a screenshot of this: + +![permission denied toast](http:/images/permission-denied-toast.png) + +Here’s the code that accomplishes this: + +``` +@Override +public void onRequestPermissionsResult(final int requestCode, + @NonNull final String[] permissions, + @NonNull final int[] grantResults) { + + if (requestCode != REQUEST_LOCATION_PERMISSION) { + return; + } + + if (permissions.length == 1 && + LOCATION_PERMISSION.equals(permissions[0]) && + grantResults[0] == PackageManager.PERMISSION_GRANTED) { + // Permission has been granted. + if (mMapFragment != null) { + mMapFragment.setMyLocationEnabled(true); + } + } else { + // Permission was denied. Display error message. + Toast.makeText(this, R.string.map_permission_denied, + Toast.LENGTH_SHORT).show(); + } + super.onRequestPermissionsResult(requestCode, permissions, + grantResults); +} +``` + +### The Test Code ### (#the-test-code) + +Let’s take a stab at testing this. Here’s what that would look like: + +``` +@Test +public void showsToastIfPermissionIsRejected() + throws Exception { + MapActivity mapActivity = new MapActivity(); + + mapActivity.onRequestPermissionsResult( + MapActivity.REQUEST_LOCATION_PERMISSION, + new String[]{MapActivity.LOCATION_PERMISSION}, new int[]{ + PackageManager.PERMISSION_DENIED}); + + assertToastDisplayed(); +} +``` + +Hopefully, you’re wondering what the implementation of `assertToastDisplayed()` looks like. Here’s the thing: there isn’t a straight forward implementation of that method. In order to implement without refactoring our code, we’d need to use a combination of roboelectric and powermock. + +However, since we are trying to listen to our tests and [change the way we write code, rather than change the way we write tests](/post/why-i-dont-use-roboletric/), we are going to stop for a moment and think about what this test is trying to tell us: + +> Our presentation logic that lives inside of `MapActivity` is tightly coupled with `Toast`. + +This coupling is what drives us to use roboelectric to give us mocked android behavior and powermock to mock the static `Toast.makeText` method. Instead, let’s listen to our test and remove the coupling. + +To guide our refactoring, let’s write our test first. This will ensure that our *new* classes are loosely coupled. We have to create a new class in this particular case in order to avoid Roboelectric, but ordinarily, we could just refactor already existing classes to reduce coupling. + +``` +@Test +public void displaysErrorWhenPermissionRejected() throws Exception { + + OnPermissionResultListener onPermissionResultListener = + new OnPermissionResultListener(mPermittedView); + + onPermissionResultListener.onPermissionResult( + MapActivity.REQUEST_LOCATION_PERMISSION, + new String[]{MapActivity.LOCATION_PERMISSION}, + new int[]{PackageManager.PERMISSION_DENIED}); + + verify(mPermittedView).displayPermissionDenied(); +} +``` + +We’ve introduced a `OnPermissionResultListener` whose job is just to handle the result of request permission from a user. Here’s the code for that: + +``` +void onPermissionResult(final int requestCode, + final String[] permissions, final int[] grantResults) { + if (requestCode != MapActivity.REQUEST_LOCATION_PERMISSION) { + return; + } + + if (permissions.length == 1 && + MapActivity.LOCATION_PERMISSION.equals(permissions[0]) && + grantResults[0] == PackageManager.PERMISSION_GRANTED) { + // Permission has been granted. + mPermittedView.displayPermittedView(); + + } else { + // Permission was denied. Display error message. + mPermittedView.displayPermissionDenied(); + } +} +``` + +The calls to `MapFragment` and `Toast` have been replaced with method calls on the `PermittedView`, an object that gets passed in through the constructor. `PermittedView` is an interface: + +``` +interface PermittedView { + void displayPermissionDenied(); + + void displayPermittedView(); +} +``` + +And it gets implemented by the `MapActivity`: + +``` +public class MapActivity extends BaseActivity + implements SlideableInfoFragment.Callback, MapFragment.Callbacks, + ActivityCompat.OnRequestPermissionsResultCallback, + OnPermissionResultListener.PermittedView { + @Override + public void displayPermissionDenied() { + Toast.makeText(MapActivity.this, R.string.map_permission_denied, + Toast.LENGTH_SHORT).show(); + } +} +``` + +This may not the *best* solution, but it gets us to a point where we can test things. This *required* that `OnPermissionResultListener` be loosely coupled with its `PermittedView`. Loose coupling == definitely an improvement. + +### Who cares? ### + +At this point, some readers might be skeptical. “Is this definitely an improvement?,” they may wonder to themselves. Here are two reasons why this *design* is better. + +(Neither reason I give, you’ll notice is “the design is better because its testable.” That would be circular reasoning.) + +#### Easier Changes #### + +First, its going to be easier to change this code now that it consists of loosely coupled components, and here’s the kicker: the code that we’ve just tested from the Google I/O app *actually did change*, and with the tests that we have in place, making those changes will be easier. The code I tested was from [an older commit](https://github.com/google/iosched/blob/bd31a838ce4ddc123c71025c859959517c7ae178/android/src/main/java/com/google/samples/apps/iosched/map/MapActivity.java). Later on, the folks working on the I/O app decided to replace the `Toast` with a `Snackbar`: + +![snackbar permission rejected](http:/images/permission-denied-snackbar.png) + +Its a small change, but because we’ve separated `OnPermissionResultListener` from `PermittedView`, we can make the change on the `MapActivity`s implementation of `PermittedView` without having to think at all about the `OnPermissionResultListener`. + +Here’s what that change would have looked like, using their little `PermissionUtils` class they wrote for displaying `SnackBar`s. + +``` +@Override +public void displayPermissionDenied() { + PermissionsUtils.displayConditionalPermissionDenialSnackbar(this, + R.string.map_permission_denied, new String[]{LOCATION_PERMISSION}, + REQUEST_LOCATION_PERMISSION); +} +``` + +Again, notice that we can make this change without thinking about the `OnPermissionResultListener` at all. This is actually exactly what Larry Constantine was talking about when he first defined the concept of coupling back in the 70s: + +> what we are striving for is loosely coupled systems…in which one can study (or debug, or maintain) any one module without having to know very much about any other modules in the system +> +> –Edward Yourdon and Larry Constantine, Structured Design + +#### Reducing Duplication #### + +Here’s another interesting reason to why the fact that our tests have forced us to remove coupling is a good thing: coupling often leads to duplication. Here’s Kent Beck on this: + +> Dependency is the key problem in software development at all scales…if dependency is the problem, duplication is the symptom. +> +> -Kent Beck, TDD By Example, pg 7. + +If this is true, when we remove coupling, we will often see opportunities to reduce duplication. Indeed, this is precisely what we find in this case. It turns out that there is another classes whose `onRequestPermissionsResult` is nearly identical to the one in `MapActivity`: [`AccountFragment`](https://github.com/google/iosched/blob/bd31a838ce4ddc123c71025c859959517c7ae178/android/src/main/java/com/google/samples/apps/iosched/welcome/AccountFragment.java#L139). Our tests drove us to create two classes `OnPermissionResultListener` and `PermittedView` that – without much modification – can be reused in these other classes. + +### Conclusion ### + +So, when we have a hard time testing our `Activity`s and `Fragment`s, its often because our tests are trying to tell us that our code is tightly coupled. The test’s warning about coupling often come in the form of an inability to make an assertion against the code we’re trying to test.1 + +When we listen to our tests, instead of changing them by using Roboelectric our powermock, we’re lead to change in our code in a way that makes it less coupled, which makes it easier to make changes and opens up opportunities to reduce duplication. + +### Notes ### + +1. It could also show up as an inability to get your target code into the right state for testing. That’s what we saw [in this post](in this post), for example. + +### We're hiring mid-senior Android developers at [Unikey](http://www.unikey.com/). Email me if you want to work for a Startup in the smart lock space in Orlando ### +--- + +> [掘金翻译计划](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)、[React](https://github.com/xitu/gold-miner#react)、[前端](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)。 From 0dbf71b08bba65f45d36a1af644dfc8018e1a4af Mon Sep 17 00:00:00 2001 From: xiaoyusilen Date: Mon, 17 Apr 2017 23:41:52 +0800 Subject: [PATCH 193/638] Change translation details and add proofreader informations --- TODO/anatomy-of-a-function-call-in-go.md | 14 +++++++------- TODO/go-function-calls-redux.md | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/TODO/anatomy-of-a-function-call-in-go.md b/TODO/anatomy-of-a-function-call-in-go.md index 4fa70afdb03..c5df8c95dab 100644 --- a/TODO/anatomy-of-a-function-call-in-go.md +++ b/TODO/anatomy-of-a-function-call-in-go.md @@ -2,11 +2,11 @@ > * 原文作者:[Phil Pearl](https://syslog.ravelin.com/@philpearl?source=post_header_lockup) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 译者:[xiaoyusilen](http://xiaoyu.world) -> * 校对者:[1992chenlu](https://github.com/1992chenlu) +> * 校对者:[1992chenlu](https://github.com/1992chenlu),[Zheaoli](https://github.com/Zheaoli) # 解析 Go 中的函数调用 # -让我们来看一些简单的 Go 的函数,然后看看我们能否明白函数调用是怎么回事。我们将通过分析 Go 编译器根据函数生成的汇编语言来完成这件事。对于一个小小的博客来说这可能有一点野心,但是别担心,汇编语言很简单。哪怕是 CPU 都能读懂。 +让我们来看一些简单的 Go 的函数,然后看看我们能否明白函数调用是怎么回事。我们将通过分析 Go 编译器根据函数生成的汇编来完成这件事。对于一个小小的博客来讲,这样的目标可能有点不切实际,但是别担心,汇编语言很简单。哪怕是 CPU 都能读懂。 @@ -20,9 +20,9 @@ func add(a, b int) int { } ``` -我们编译的时候需要关闭优化,这样方便理解每个部分。我们用 `go build -gcflags 'N -l'` 这个命令来完成上述操作。然后我们可以用 `go tool objdump -s main.add func` 输出我们函数的具体细节(这里的 func 是我们的包名,也就是我们刚刚用 go build 编译出的可执行文件)。 +我们编译的时候需要关闭优化,这样方便我们去理解生成的汇编代码。我们用 `go build -gcflags 'N -l'` 这个命令来完成上述操作。然后我们可以用 `go tool objdump -s main.add func` 输出我们函数的具体细节(这里的 func 是我们的包名,也就是我们刚刚用 go build 编译出的可执行文件)。 -如果你以前从来没有看过汇编语言,那么恭喜啦,你今天看到的内容对你来说是全新的。我将在 Mac 上完成这些事情,所以配置是 Intel 64位。 +如果你之前没有学过汇编,那么恭喜你,你将接触到一个全新的事物。另外我会在 Mac 上完成这篇博客的代码,因此所生成的是 Intel 64-bit 汇编。 ``` main.go:20 0x22c0 48c744241800000000 MOVQ $0x0, 0x18(SP) @@ -33,7 +33,7 @@ func add(a, b int) int { main.go:21 0x22db c3 RET ``` -我们看到了什么?每一行如下所示被分为了4部分: +现在我们看到了什么?如下所示,每一行被分为了4部分: - 源文件的名称和行号(main.go:15)。这行的源代码会被转换为标有代码行号的说明。Go 的一行可能被转换成多行程序集。 - 目标文件中的偏移量(例如 0x22C0)。 @@ -51,8 +51,8 @@ func add(a, b int) int { 现在让我们从第一条指令开始看每一条内容。别忘了我们需要从内存中加载两个参数 `a` 和 `b`,把它们相加,然后返回至调用函数。 -1. `MOVQ $0x0, 0x18(SP)` 将 0 置于存储单元 SP+0x18 中。 这句看起来有点神秘。 -2. `MOVQ 0x8(SP), AX` 将存储单元 SP+0x8 中的内容放到 CPU 寄存器 AX 中。也许这就是从内存中加载的我们的参数之一? +1. `MOVQ $0x0, 0x18(SP)` 将 0 置于存储单元 SP+0x18 中。 这句代码看起来有点抽象。 +2. `MOVQ 0x8(SP), AX` 将存储单元 SP+0x8 中的内容放到 CPU 寄存器 AX 中。也许这就是从内存中加载的我们所使用的参数之一? 3. `MOVQ 0x10(SP), CX` 将存储单元 SP+0x10 的内容置于 CPU 寄存器 CX 中。 这可能就是我们所需的另一个参数。 4. `ADDQ CX, AX` 将 CX 与 AX 相加,将结果存到 AX 中。好,现在已经把两个参数相加了。 5. `MOVQ AX, 0x18(sp)` 将寄存器 AX 的内容存储在存储单元 SP+0x18 中。这就是在存储相加的结果。 diff --git a/TODO/go-function-calls-redux.md b/TODO/go-function-calls-redux.md index b8717411ff1..dad571a5324 100644 --- a/TODO/go-function-calls-redux.md +++ b/TODO/go-function-calls-redux.md @@ -2,13 +2,13 @@ > * 原文作者:[Phil Pearl](https://hackernoon.com/@philpearl?source=post_header_lockup) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 译者:[xiaoyusilen](http://xiaoyu.world) -> * 校对者:[1992chenlu](https://github.com/1992chenlu) +> * 校对者:[1992chenlu](https://github.com/1992chenlu),[Zheaoli](https://github.com/Zheaoli) # Go 函数调用 Redux # -前段时间在一篇[文章](https://syslog.ravelin.com/anatomy-of-a-function-call-in-go-f6fc81b80ecc#.gpqsgzmjc)中我答应写一篇进一步分析 Go 中如何进行函数调用和调用堆栈在 Go 中如何工作的文章。现在我找到了一种简洁的方式来向大家展示上述内容,所以有了现在这篇文章。 +前段时间在一篇[文章](https://syslog.ravelin.com/anatomy-of-a-function-call-in-go-f6fc81b80ecc#.gpqsgzmjc)中我答应写一篇进一步分析 Go 中如何进行函数调用和堆栈调用在 Go 中如何工作的文章。现在我找到了一种简洁的方式来向大家展示上述内容,所以有了现在这篇文章。 -什么是调用堆栈?它是一个用于保存局部变量和调用参数的内存区域,并且跟踪每个函数应该返回到哪里去。每个 goroutine 都有它自己的堆栈。你甚至可以说每个 goroutine 就是它自己的堆栈。 +什么是堆栈调用?它是一个用于保存局部变量和调用参数的内存区域,并且跟踪每个函数应该返回到哪里去。每个 goroutine 都有它自己的堆栈。你甚至可以说每个 goroutine 就是它自己的堆栈。 下面是我用于演示堆栈的代码。就是一系列简单的函数调用,main() 函数调用 [f1(0xdeadbeef)](https://en.wikipedia.org/wiki/Hexspeak),然后调用 `f2(0xabad1dea)`,再调用 `f3(0xbaddcafe)`。然后 `f3()` 将其中一个作为它的参数,并且将它存储在名为 `local` 的本地变量中。然后获取 `local` 的内存地址并且从那里开始输出。因为 `local` 在栈内,所以输出的就是栈。 From c0c66b4c26a1b06011085d66adc994db9a6a700d Mon Sep 17 00:00:00 2001 From: zhuzi Date: Tue, 18 Apr 2017 00:00:52 +0800 Subject: [PATCH 194/638] proofreading --- TODO/secure-web-app-http-headers.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/TODO/secure-web-app-http-headers.md b/TODO/secure-web-app-http-headers.md index 5311d35ff7a..b707e2397a9 100644 --- a/TODO/secure-web-app-http-headers.md +++ b/TODO/secure-web-app-http-headers.md @@ -6,7 +6,7 @@ ## 如何使用 HTTP Headers 来保护你的 Web 应用 ## -众所周知,无论是简单的小网页还是复杂的单页应用,Web 应用都是网络攻击的目标。2016年,这种最主要的攻击模式 —— 攻击 web 应用,造成了大约 [40% 的数据泄露](http://www.verizonenterprise.com/verizon-insights-lab/dbir/2016/)。事实上,现在来说,了解网络安全并不是锦上添花, 而是 **Web 开发者的必需任务**,特别对于构建面向消费者的产品的开发人员。 +众所周知,无论是简单的小网页还是复杂的单页应用,Web 应用都是网络攻击的目标。2016 年,这种最主要的攻击模式 —— 攻击 web 应用,造成了大约 [40% 的数据泄露](http://www.verizonenterprise.com/verizon-insights-lab/dbir/2016/)。事实上,现在来说,了解网络安全并不是锦上添花,而是 **Web 开发者的必需任务**,特别对于构建面向消费者的产品的开发人员。 开发者可以利用 HTTP 响应头来加强 Web 应用程序的安全性,通常只需要添加几行代码即可。本文将介绍 web 开发者如何利用 HTTP Headers 来构建安全的应用。虽然本文的示例代码是 Node.js,但基本所有主流的服务端语言都支持设置 HTTP 响应头,并且都可以简单地对其进行配置。 @@ -85,7 +85,7 @@ HSTS 的指令如下: 这是一个强大的指令,强制浏览器**始终**安全加载你的 web 应用程序,即使是第一次收到响应之前加载!这是通过将启用 HSTS 预加载域的列表硬编码到浏览器的代码中实现的。要启用预加载功能,你需要在 Google Chrome 团队维护的网站 [HSTS 预加载列表提交](https://hstspreload.org)注册你的域。 -注意谨慎使用 `preload`,因为这意味着它不能轻易撤销,并可能更新延迟数个月。虽然预加载肯定会加强应用程序的安全性,但也意味着你需要充分确信你的应用程序可以支持仅 HTTPS! +注意谨慎使用 `preload`,因为这意味着它不能轻易撤销,并可能更新延迟数个月。虽然预加载肯定会加强应用程序的安全性,但也意味着你需要充分确信你的应用程序仅支持 HTTPS! 我建议的用法是 `Strict-Transport-Security: max-age=31536000; includeSubDomains;`,这样指示了浏览器强制通过 HTTPS 连接到源主机并且有效期为一年。如果你对你的 app 仅处理 HTTPS 很有信心,我也推荐加上 `preload` 指令,当然别忘记去前面提到的预加载列表注册你的网站。 @@ -133,7 +133,7 @@ https://mywebapp.com/search?

``` -When we take a look at Chrome, we'll see that there is not much more happening. +然后我们在 Chrome 中看看,发现并没有发生什么事。 ![image01](http://images.contentful.com/256tjdsmm689/4JHwnbyrssomECAG2GI8se/e8e35adc37bc0627f0902bcc2fdb52df/image01.png) -The bundle is loaded as before, "Hello world!" is shown, but that's it. And that's excellent, because this is how web works: browsers are forgiving, they won't throw errors when they don't understand markup we send down the wire. Chrome just ignores the script element with the type it doesn't know. +代码包还是和之前一样加载,“Hello world!” 也正常显示。虽然没看到效果,但是这说明浏览器可以接受这种它们并不理解的命令而不会报错,这是极好的。Chrome 忽略了这个它无法判断类型的 script 元素。 -Now, let's check the Safari technology preview: +接下来,让我们在 Safari technology preview 中试试: ![Bildschirmfoto 2017-03-29 um 17.06.26](http://images.contentful.com/256tjdsmm689/1mefe0J3JKOiAoSguwMkka/0d76c5666300ed0b631a0fe548ac5b52/Bildschirmfoto_2017-03-29_um_17.06.26.png) -Sadly, there is no additional "Hello world" showing up. The reason is the difference between build tools and native ES modules: whereas Webpack figures out which files to include during the build process, when running ES modules in the browser, we need to define concrete file paths. +遗憾的是,它并没有显示另外的“Hello world”。造成问题的原因是构建工具与原生 ES 模块的差异:Webpack 是在构建的过程中找到那些文件需要 include 的,而 ES 模块是在浏览器中运行的时候才去取文件的,因此我们需要为此指定正确的文件路径: ``` // app/index.js -// This needs to be changed +// 这样写不行 // import dep1 from './dep-1'; -// This works +// 这样写能正常工作 import dep1 from './dep-1.js'; ``` -The adjusted file paths work great, except for the fact that Safari preview now loads the bundle and the three individual modules, meaning that our code will be executed twice. +改了文件路径之后它能正常工作了,但事实上 Safari Preview 加载了代码包,以及三个独立的模块,这意味着我们的代码被执行了两次。 ![image02](http://images.contentful.com/256tjdsmm689/6MeIDF7GuW6gy8om4Ceccc/a0dba00a4e0f301f2a7fd65449d044ab/image02.png) -The solution is the `nomodule` attribute, which we can set on the script element requesting the bundle. This attribute [was added to the spec quite recently](https://github.com/whatwg/html/commit/a828019152213ae72b0ed2ba8e35b1c472091817) and Safari Preview supports it as of the [end of January](https://trac.webkit.org/changeset/211078/webkit). It tells Safari that this script is the "fallback" script for the lack of ES6 modules support, and in this case shouldn't be executed. +这个问题的解决方案就是加上 `nomodule` 属性,我们可以在加载代码包的 script 元素里加上这个属性。这个属性[是最近才加入标准中的](https://github.com/whatwg/html/commit/a828019152213ae72b0ed2ba8e35b1c472091817),Safari Preview 也是在[一月底](https://trac.webkit.org/changeset/211078/webkit)才支持它的。这个属性会告诉 Safari,这个 script 是当不支持 ES6 模块时的“退路”。在这个例子中,浏览器支持 ES6 模块因此加上这个属性的 script 元素中的代码将不会执行。 ``` @@ -162,22 +161,26 @@ The solution is the `nomodule` attribute, which we can set on the script element ![image03](http://images.contentful.com/256tjdsmm689/1YchZEromA2ueKUCoYqMsc/2c68c46ffd2a3ad73d99d17020d56093/image03.png) -That's good. With the combination of `type="module"` and `nomodule`, we can load a classic bundle in not supporting browsers and load JavaScript modules in supporting browsers. +现在好了。通过结合使用 `type="module"` 与 `nomodule`,我们现在可以在不支持 ES6 模块的浏览器中加载传统的代码包,在支持 ES6 模块的浏览器中加载 JavaScript 模块。 + +你可以在 [es-module-on.stefans-playground.rocks](http://es-module-on.stefans-playground.rocks/) 查看这个尚在制定的规范。 -You can check out this state in production at [es-module-on.stefans-playground.rocks](http://es-module-on.stefans-playground.rocks/). +### 模块与脚本的不同 ### -### Differences between modules and scripts ### +这儿有几个问题。首先,JavaScript 在 ES6 模块中运行与平常在 script 元素中不同。Axel Rauschmayer 在[他的探索 ES6 一书](http://exploringjs.com/es6/ch_modules.html#sec_modules-vs-scripts)中很好地讨论了这个问题。我推荐你点击上面的链接阅读这本书,但是在此我先快速地总结一下主要的不同点: -There are a few gotchas here, though. First of all, JavaScript running in an ES6 module is not quite the same as in a regular script element. Axel Rauschmayer covers this quite nicely in [his book Exploring ES6](http://exploringjs.com/es6/ch_modules.html#sec_modules-vs-scripts). I highly recommend you check it out but let's just quickly mention the main differences: +- ES6 模块默认在严格模式下运行(因此你不需要加上 `use strict` 了)。 +- `this` 指向的最高一级对象是 `undefined`(而不是 window)。 +- 最高级变量是 module 的局部变量(而不是 global)。 +- ES6 模块会在浏览器完成 HTML 的分析之后异步加载与执行。 -- ES6 modules are running in strict mode by default (no need for `'use strict'` anymore). -- Top-level value of `this` is `undefined`. -- Top-level variables are local to the module. -- ES6 modules are loaded and executed asynchronously after the browser finished parsing the HTML. +我认为,这些特性是巨大进步。模块是局部的——这意味着我们不再需要到处使用 IIFE 了,而且我们不用再担心全局变量泄露。而且默认在严格模式下运行,意味着我们可以在很多地方抛弃 `use strict` 声明。 -In my opinion, these are all huge advantages. Modules are local – there is no need for IIFEs around everything, and also we don't have to fear global variable leaking anymore. Also running in strict mode by default means that we can drop a lot of `'use strict'` statements. +> 译注:IIFE 全称 immediately-invoked function expression,即立即执行函数,也就是大家熟知的在函数后面加括号。 -And from a performance point of view (probably the most important one) **modules load and execute deferred by default**. So we won't accidentally add blocking scripts to our website and there is no [SPOF](https://www.stevesouders.com/blog/2010/06/01/frontend-spof/) issue when dealing with script `type="module"` elements. We could place an `async` attribute on it, which overwrites the default deferred behavior, but `defer`[is a good choice these days](https://calendar.perfplanet.com/2016/prefer-defer-over-async/). +从改善性能的观点来看(可能是最重要的进步),**模块默认会延迟加载与执行**。因此我们将不再会不小心给我们的网站加上了阻碍加载的代码,使用 `type="module"` 的 script 元素也不再会有 [SPOF](https://www.stevesouders.com/blog/2010/06/01/frontend-spof/) 问题。我们也可以给它加上一个 `async` 属性,它将会覆盖默认的延迟加载行为。不过使用 `defer` [在现在也是一个不错的选择](https://calendar.perfplanet.com/2016/prefer-defer-over-async/)。 + +> 译注:SPOF 全称 Single Points Of Failure——单点故障 ``` @@ -194,13 +197,13 @@ And from a performance point of view (probably the most important one) **modules ``` -In case you want to check the details around that, the [script element spec](https://html.spec.whatwg.org/multipage/scripting.html#the-script-element) is an understandable read and includes some examples. +如果你想详细了解这方面内容,可以阅读 [script 元素说明书](https://html.spec.whatwg.org/multipage/scripting.html#the-script-element),这篇文章简单易读,并且包含了一些示例。 -## Minifying of pure ES6 ## +## 压缩纯 ES6 代码 ## -But we're not quite there yet! We serve a minified bundle for Chrome and individual not minified files for Safari Preview now. How can we make these smaller? UglifyJS should do the job just fine, right? +还没完!我们现在能为 Chrome 提供压缩过的代码包,但是还不能为 Safari Preview 提供单独压缩过的文件。我们如何让这些文件变得更小呢?UglifyJS 能完成这项任务吗? -It turns out that UglifyJS is not able to fully deal with ES6 code yet. There is a `harmony` development branch available, but unfortunately it didn't work with my three JavaScript files at the time of writing. +然而必须指出,UglifyJS 并不能完全处理好 ES6 代码。虽然它有个 `harmony` 分支([地址](https://github.com/mishoo/UglifyJS2/tree/harmony))支持ES6,但不幸的是在我写这 3 个 JavaScript 文件的时候它并不能正常工作。 ``` $ uglifyjs dep-1.js -o dep-1.min.js @@ -212,12 +215,11 @@ SyntaxError: Unexpected token: punc (() FAIL: 1 ``` +但是现在 UglifyJS 几乎存在于所有工具链中,那全部使用 ES6 编写的工程应该怎么办呢? -But UglifyJS is in every toolchain today, how does this work for all the projects written in ES6 out there? - -The usual flow is that tools like Babel transpile to ES5, and then Uglify comes into play to minify this ES5 code. I want to ignore ES5 transpilation in this article: we're dealing with the future here, Chrome has [97% ES6 coverage](https://kangax.github.io/compat-table/es6/#chrome59) and Safari Preview has already fabulous [100% ES6 coverage since version 10](https://kangax.github.io/compat-table/es6/#safari10_1). +通常的流程是使用 Babel 之类的工具将代码转换为 ES5,然后使用 Uglify 对 ES5 代码进行压缩处理。但是在这篇文章里我不想使用 ES5 翻译工具,因为我们现在是要寻找面向未来的处理方式!Chrome 已经[覆盖了 97% ES6 规范](https://kangax.github.io/compat-table/es6/#chrome59) ,而 Safari Preview 版[自 verion 10 之后已经 100% 很好地支持 ES6](https://kangax.github.io/compat-table/es6/#safari10_1)了。 -I asked the Twittersphere if there is a minifier available that can deal with ES6, and [Lars Graubner](https://twitter.com/larsgraubner) pointed me towards [Babili](https://github.com/babel/babili). Using Babili, we can easily minify the ES6 modules. +我在推特中提问是否有能够处理 ES6 的压缩工具,[Lars Graubner](https://twitter.com/larsgraubner) 告诉我可以使用 [Babili](https://github.com/babel/babili)。使用 Babili,我们能够轻松地对 ES6 模块进行压缩。 ``` @@ -231,7 +233,7 @@ export default function() { export default function(){return 'Hello World. dependencies loaded.'} ``` -With the Babili CLI tool, it's almost too easy to minify all the files separately. +使用 Babili CLI 工具,可以轻松地分别压缩各个文件。 ``` $ babili app -d dist/modules @@ -240,7 +242,7 @@ app/dep-2.js -> dist/modules/dep-2.js app/index.js -> dist/modules/index.js ``` -The result looks then as follows. +最终结果: ``` $ ll dist @@ -252,19 +254,19 @@ $ ll dist/modules -rw-r--r-- 1 stefanjudis staff 161B Mar 16 22:32 index.js ``` -The bundle is still roughly around 850B, and all the files are around 300B in total. I'm ignoring GZIP compression here as [it doesn't work well on such small file sizes](http://webmasters.stackexchange.com/questions/31750/what-is-recommended-minimum-object-size-for-gzip-performance-benefits) (we'll get back to that later). +代码包仍然是大约 850B,所有文件加起来大约是 300B。我没有使用 GZIP,因为[它并不能很好地处理小文件](http://webmasters.stackexchange.com/questions/31750/what-is-recommended-minimum-object-size-for-gzip-performance-benefits)。(我们稍后会提到这个) -## Speeding up ES6 modules with rel=preload? ## +## 能通过 rel=preload 来加速 ES6 的模块加载吗? ## -The minification of the single JS files is a huge success. It's 298B vs. 856B, but we could even go further and speed things up more. Using ES6 modules we are now able to ship less code, but looking at the waterfall again we'll see that the requests are made sequentially because of the defined dependency chain of the modules. +对单个 JS 文件进行压缩取得了很好的效果。文件大小从 856B 降低到了 298B,但是我们还能进一步地加快加载速度。通过使用 ES6 模块,我们可以装载更少的代码,但是看看瀑布图你会发现,request 会按照模块的依赖链一个一个连续地加载。 -What if we could throw `` elements in the mix which can be used to tell the browser upfront that additionally requests will be made soon? We have build tool plugins like Addy Osmani's [Webpack preload plugin](https://github.com/GoogleChrome/preload-webpack-plugin) for code splitting already – is something like this possible for ES6 modules? In case you don't know how `rel="preload"` works, you should check out the [article on this topic](https://www.smashingmagazine.com/2016/02/preload-what-is-it-good-for/) by Yoav Weiss on Smashing Magazine. +那如果我们像之前在浏览器中对代码进行预加载那样,用 `` 元素告知浏览器要加载额外的 request,是否会加快模块的加载速度呢?在 Webpack 中,我们已经有了类似的工具,比如 Addy Osmani 的 [Webpack 预加载插件](https://github.com/GoogleChrome/preload-webpack-plugin)可以对分割的代码进行预加载,那 ES6 模块有没有类似的方法呢?如果你还不清楚 `rel="preload"` 是如何运作的,你可以先阅读 Yoav Weiss 在 Smashing Magazine 发表的相关文章:[点击阅读](https://www.smashingmagazine.com/2016/02/preload-what-is-it-good-for/) -Unfortunately, preloading of ES6 modules is not so easy because they behave differently than normal scripts. The question is how a link element with a set `rel="preload"` attribute should treat an ES6 module? Should it fetch all the dependent files, too? This is an obvious question to answer, but there are more browser internal problems to solve, too, if module treatment should go into the `preload` directive. In case you're interested in this topic [Domenic Denicola](https://twitter.com/domenic) discusses these problems [in a GitHub issue](https://github.com/whatwg/fetch/issues/486), but it turns out that there are too many differences between scripts and modules to implement ES6 module treatment in the `rel="preload"` directive. The solution might be another `rel="modulepreload"` directive to clearly separate functionalities, with [the spec pull request](https://github.com/whatwg/html/pull/2383) pending at the time of writing, so let's see how we'll preload modules in the future. +但是,ES6 模块的预加载并不是那么简单,他们与普通的脚本有很大的不同。那么问题来了,对一个 link 元素加上 `rel="preload"` 将会怎样处理 ES6 模块呢?它也会取出所有的依赖文件吗?这个问题显而易见(可以),但是使用 `preload` 命令加载模块,需要解决更多浏览器的内部实现问题。[Domenic Denicola](https://twitter.com/domenic) 在[一个 GitHub issue](https://github.com/whatwg/fetch/issues/486) 中讨论了这方面的问题,如果你感兴趣的话可以点进去看一看。但是事实证明,使用 `rel="preload"` 加载脚本与加载 ES6 模块是截然不同的。可能以后最终的解决方案是用另一个 `rel="modulepreload"` 命令来专门加载模块。在本文写作时,[这个 pull request](https://github.com/whatwg/html/pull/2383) 还在审核中,你可以点进去看看未来我们可能会怎样进行模块的预加载。 -## Bringing in real dependencies ## +## 加入真实情况中的依赖 ## -Three files don't make a real app, so let's add a real dependency. Fortunately, [Lodash](https://lodash.com/) offers all of its functionality also in split ES6 modules, which I then minified using Babili. So let's modify the `index.js` file to also include a Lodash method. +仅仅 3 个文件当然没法做一个真正的 app,所以让我们给它加一些真实情况中的依赖。[Lodash](https://lodash.com/) 根据 ES6 模块对它的功能进行了分割,并分别提供给用户。我取出其中一个功能,然后使用 Babili 进行压缩。现在让我们对 `index.js` 文件进行修改,引入这个 Lodash 的方法。 ``` @@ -281,29 +283,29 @@ function getComponent() { document.body.appendChild(getComponent()); ``` -The use of `isEmpty` is trivial in this case, but let's see what happens now after adding this dependency. +在这个例子中,`isEmpty` 基本上没有被使用,但是在加上它的依赖后,我们可以看看发生了什么: ![image07](http://images.contentful.com/256tjdsmm689/13F95Xpl32Mu0MgE0mgS2o/c9dbc002e53bf56ee0eeb0df40b55f9c/image07.png) -The request count went up to over 40, the page load time went up from roughly 100ms to something between 400ms and 800ms on a decent wifi connection, and the shipped overall size increased to approximately 12KB without compression. Unfortunately, Safari Preview is not available on [WebPagetest](https://www.webpagetest.org/) to run some reliable benchmarks. +可以看到 request 数量增加到了 40 个以上,页面在普通 wifi 下的加载时间从大约 100 毫秒上升到了 400 到 800 毫秒,加载的数据总大小在没有压缩的情况下增加到了大约 12KB。可惜的是 [WebPagetest](https://www.webpagetest.org/) 在 Safari Preview 中不可用,我们没法给它做可靠的标准检测。 -Chrome receiving the bundled JavaScript, on the other hand, is at a slim ~8KB file size. +但是,Chrome 收到打包后的 JavaScript 数据比较小,只有大约 8KB。 ![image05](http://images.contentful.com/256tjdsmm689/6xxfWBW9nqAeqQ8ck0MqU/62a74102e9247d785a61a84766356f51/image05.png) -This 4KB difference is definitely something to check. You can find this example at [lodash-module-on.stefans-playground.rocks](https://lodash-module-on.stefans-playground.rocks/). +这 4KB 的差距是不能忽视的。你可以在 [lodash-module-on.stefans-playground.rocks](https://lodash-module-on.stefans-playground.rocks/) 找到本示例。 -### Compression works only well on larger files ### +### 压缩工作仅对大文件表现良好 ### -In case you looked closely at the screenshots of the Safari developer tools, you might have noticed that the transferred file size was actually bigger than the source. Especially in a large JavaScript app, including a lot of small chunks makes a big difference and that's because GZIP doesn't play well with small file sizes. +如果你仔细看上面 Safari 开发者工具的截图,你可能会注意到转换后的文件大小其实比源码还要大。在很大的 JavaScript app 中这个现象会更加明显,一堆的小 Chunk 会造成文件大小的很大不同,因为 GZIP 并不能很好地压缩小文件。 -Khan Academy [discovered the same thing](http://engineering.khanacademy.org/posts/js-packaging-http2.htm) a while ago when experimenting with HTTP/2. The idea of shipping smaller files is great to guarantee perfect cache hit ratios, but at the end, it's always a tradeoff and it's depending on several factors. For a large code base splitting the code into several chunks (a *vendor* and an *app* bundle) makes sense, but shipping thousands of tiny files that can't be compressed properly is not the right approach. +Khan Academy 在前一段时间[探究了同样的问题](http://engineering.khanacademy.org/posts/js-packaging-http2.htm),他是用 HTTP/2 进行研究的。装载更小的文件能够很好地确保缓存命中率,但到最后它一般都会作为一个权衡方案,而且它的效果会被很多因素影响。对于一个很大的代码库来说,分解成若干个 chunk(一个 *vendor* 文件和一个 app bundle)是理所当然的,但是要装载数千个不能被压缩的小文件可能并不是一种明智的方法。 -### Tree shaking is the cool kid in town ### +### Tree shaking 是个超 COOL 的技术 ### -Another thing to point out is that thanks to the relatively new tree shaking mechanism, build processes can eliminate code that's not used and imported by any other module. The first build tool that supported this was Rollup, but now Webpack in version 2 supports it as well — [as long as we disable the `module` option in babel](https://medium.freecodecamp.com/tree-shaking-es6-modules-in-webpack-2-1add6672f31b#22c4). +必须要说:感谢非常新潮的 tree shaking 技术,通过它,构建进程可以将没有使用过以及没有被其它模块引用的代码删除。第一个支持这个技术的构建工具是 Rollup,现在 Webpack 2 也支持它——[只要我们在 babel 中禁用 `module` 选项](https://medium.freecodecamp.com/tree-shaking-es6-modules-in-webpack-2-1add6672f31b#22c4)。 -Let's say we changed `dep-2.js` to include things that won't be imported by `dep-1.js`. +我们试着改一改 `dep-2.js`,让它包含一些不会在 `dep-1.js` 中使用的东西。 ``` export default function() { @@ -315,20 +317,20 @@ export const unneededStuff = [ ]; ``` -Babili will simply minify the file and Safari Preview, in this case, would receive several code lines that are not used. A Webpack or Rollup bundle, on the other hand, won't include `unneededStuff`. Tree shaking offers huge savings that definitely should be used in a real production code base. +Babili 将会简单地压缩文件,比如 Safari Preview 还是会接收到这几行没有用过的代码。而另一方面,Webpack 打的包或者 Rollup 打的包将不会包含这个 `unnededStuff`。Tree shaking 省略了大量代码,毫无疑问,它能够在真实的产品代码库中很好地发挥作用。 -## The future looks bright, but build processes are here to stay ## +## 尽管未来很明朗,但是现在的构建过程仍然不会变动 ## -So, ES6 modules are on their way, but it doesn't look like anything will change when they finally arrive in all the major browsers. We won't start shipping thousands of tiny files to guarantee good compression, and we won't abandon build processes to make use of tree shaking and dead code elimination. **Frontend development is and will be as complicated as always**. +ES6 模块即将到来,但是直到它最终在各大主流浏览器中实现前,我们的开发并不会发生什么变化。我们既不会装载一堆小文件来确保压缩率,也不会为了使用 tree shaking 和死码删除来抛弃构建过程。**前端开发现在及将来都会一如既往地复杂**。 -**The most important thing to remember is that measuring is the key to succees**. Don't split everything and assume that it will lead to an improvement. Just because we might have support for ES6 modules in browsers soon, it doesn't mean that we can get rid of a build process and a proper "bundle strategy". Here at Contentful we'll stick to our build processes, and continue to ship bundles including our [JavaScript SDKs](https://www.contentful.com/developers/docs/javascript/). +不要把所有东西都进行分割然后就假设它会改善性能。我们即将迎来 ES6 模块的浏览器原生支持,但是这不意味着我们可以抛弃构建过程与合适的打包策略。在我们 Contentful 这儿,将继续坚持我们的构建过程,以及继续使用我们的 [JavaScript SDKs](https://www.contentful.com/developers/docs/javascript/) 进行打包。 -Yet, I have to admit that Frontend development still feels great. JavaScript evolves, and we'll finally have a way to deal with modules baked into the language. I can't wait to see how and if this influences the JavaScript ecosystem and what the best practices will be in a couple of years. +然而,我们必须承认现在前端的开发体验仍然良好。JavaScript 仍在进步,最终我们将能使用真正融入语言的模块。在几年后,原生模块对 JavaScript 生态的影响以及最佳实践方法将会是怎样的呢?让我们拭目以待。 -## Additional resources ## +## 其它资源 ## -- [Article series on ES6 modules](https://blog.hospodarets.com/native-ecmascript-modules-the-first-overview) by Serg Hospodarets -- [The modules chapter](http://exploringjs.com/es6/ch_modules.html) in "[Exploring ES6](http://exploringjs.com/)" +- [ES6 模块系列文章](https://blog.hospodarets.com/native-ecmascript-modules-the-first-overview) 作者:Serg Hospodarets +- [《探索 ES6》](http://exploringjs.com/) 的 [模块章节](http://exploringjs.com/es6/ch_modules.html) --- From 7d63868343567bca4bbf3b66766b09a8b9f780f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=B9=E5=8F=B7=E4=B8=89?= Date: Tue, 25 Apr 2017 23:27:20 +0800 Subject: [PATCH 259/638] Create familiarity-bias-is-holding-you-back-its-time-to-embrace-arrow-functions.md --- ...ack-its-time-to-embrace-arrow-functions.md | 306 ++++++++++++++++++ 1 file changed, 306 insertions(+) create mode 100644 TODO/familiarity-bias-is-holding-you-back-its-time-to-embrace-arrow-functions.md diff --git a/TODO/familiarity-bias-is-holding-you-back-its-time-to-embrace-arrow-functions.md b/TODO/familiarity-bias-is-holding-you-back-its-time-to-embrace-arrow-functions.md new file mode 100644 index 00000000000..6e84b0f8352 --- /dev/null +++ b/TODO/familiarity-bias-is-holding-you-back-its-time-to-embrace-arrow-functions.md @@ -0,0 +1,306 @@ +> * 原文地址:[Familiarity Bias is Holding You Back: It’s Time to Embrace Arrow Functions](https://medium.com/javascript-scene/familiarity-bias-is-holding-you-back-its-time-to-embrace-arrow-functions-3d37e1a9bb75) +> * 原文作者:[Eric Elliott](https://medium.com/@_ericelliott?source=post_header_lockup) +> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) +> * 译者: +> * 校对者: + +# Familiarity Bias is Holding You Back: It’s Time to Embrace Arrow Functions # + +![](https://cdn-images-1.medium.com/max/800/1*Dwv24VW3sEuGBo4BqrsRQg.jpeg) + +“Anchor” — Actor212 — (CC BY-NC-ND 2.0) + +I teach JavaScript for a living. Recently I’ve shuffled around my curriculum to teach curried arrow functions sooner — within the first few lessons. I moved it earlier in the curriculum because it’s an extremely valuable skill, and students pick up currying with arrows **a lot quicker** than I thought they would. + +If they can understand it and take advantage of it earlier, why not teach it earlier? + +> Note: My courses are not designed for people who have never touched a line of code before. Most students join after spending at least a few months coding — on their own, in a bootcamp, or professionally. However, I have seen many junior developers with little or no experience pick these topics up quickly. + +I’ve seen a bunch of students get a working familiarity with curried arrow functions within the span of a single 1-hour lesson. (If you’re a member of [“Learn JavaScript with Eric Elliott”](https://ericelliottjs.com/product/lifetime-access-pass/), you can watch the 55-minute [ES6 Curry & Composition](https://ericelliottjs.com/premium-content/es6-curry-composition/). + +Seeing how quickly students pick it up and start wielding their new-found curry powers, I’m always a bit surprised when I post curried arrow functions on Twitter, and the Twitterverse responds with outrage at the thought of inflicting that “unreadable” code on the people who will need to maintain it. + +First, let me give you an example of what we’re talking about. The first time I noticed the backlash was the Twitter response to this function: + +``` +const secret = msg => () => msg; +``` + +I was shocked when people on Twitter accused me of trying to confuse people. I wrote that function to demonstrate how **easy** it is to express curried functions in ES6. It is the **simplest** practical application and expression of a closure that I can think of in JavaScript. (Related: [“What is a Closure?”](https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-closure-b2f0d2152b36)). + +It’s equivalent to the following function expression: + +``` +const secret = function (msg) { + return function () { + return msg; + }; +}; +``` + +`secret()` is a function which takes a `msg` and returns a new function which returns the `msg`. It takes advantage of closures to fix the value of `msg` to whatever value you pass into `secret()`. + +Here’s how you use it: + +``` +const mySecret = secret('hi'); +mySecret(); // 'hi' +``` + +It turns out, the “double arrow” is what confused people. I’m convinced that this is a fact: + +> With familiarity, in-line arrow functions are the **most readable** way to express curried functions in JavaScript. + +Many people have argued to me that the longer form is easier to read than the shorter form. They’re partly right, but mostly wrong. It’s more verbose, and more explicit, but not easier to read — at least, not to somebody **familiar** with arrow functions. + +The objections I saw on Twitter just weren’t jiving with the smooth learning experience my students were enjoying. In my experience, students take to curried arrow functions like fish take to water. Within days of learning them, they are one with the arrows. They sling them effortlessly to tackle all sorts of coding challenges. + +I don’t see any sign that arrow functions are “hard” for them to learn, to read, or to understand — once they have made the initial investment of learning them over the course of a few 1-hour lessons and study sessions. + +They easily read curried arrow functions they have never seen before and explain to me what’s going on. They naturally write their own when I present a challenge to them. + +In other words, as soon as they become **familiar** with seeing curried arrow functions, they have **no trouble **with them. They read them as easily as you are reading this sentence — and their understanding is reflected in much simpler code with fewer bugs. + +### Why Some People Think Legacy Function Expressions Look “Easier” to Read ### + +**Familiarity bias** is a measurable [human cognitive bias](https://www.psychologytoday.com/blog/mind-my-money/200807/familiarity-bias-part-i-what-is-it) that leads us to make self-destructive decisions despite being aware of a better option. We keep using the same old patterns in spite of knowing about better patterns out of comfort and habit. + +You can learn a lot more about familiarity bias (and a lot of other ways we fool ourselves) from the excellent book, [“The Undoing Project: A Friendship that Changed Our Minds”](https://www.amazon.com/Undoing-Project-Friendship-Changed-Minds-ebook/dp/B01GI6S7EK/ref=as_li_ss_tl?ie=UTF8&qid=1492606452&sr=8-1&keywords=the+undoing+project&linkCode=ll1&tag=eejs-20&linkId=4ebd1476f97023e8acb4bba37ea18b90). This book should be required reading for every software developer, because it encourages you to think more critically and test your assumptions in order to avoid falling into a variety of cognitive traps — and the story of how those cognitive traps were discovered is really good, too. + +### Legacy Function Expressions are Probably Causing Bugs in Your Code ### + +Today I was rewriting a curried arrow function from ES6 to ES5 so that I could publish it as an open-source module that people could use in old browsers without transpiling. The ES5 version shocked me. + +The ES6 version was simple, short, and elegant — only 4 lines. + +I thought for sure, **this** was the function that would prove to Twitter that arrow functions are superior, and that people should abandon their legacy functions like the bad habit they are. + +So I tweeted: + +[![Markdown](http://i2.muimg.com/1949/15826825ba3ae5a9.png)](https://twitter.com/_ericelliott/status/854608052967751680/photo/1) + +Here’s the text of the functions, in case the image isn’t working for you: + +``` +// curried with arrows +const composeMixins = (...mixins) => ( + instance = {}, + mix = (...fns) => x => fns.reduce((acc, fn) => fn(acc), x) +) => mix(...mixins)(instance); +// vs ES5-style +var composeMixins = function () { + var mixins = [].slice.call(arguments); + return function (instance, mix) { + if (!instance) instance = {}; + if (!mix) { + mix = function () { + var fns = [].slice.call(arguments); + return function (x) { + return fns.reduce(function (acc, fn) { + return fn(acc); + }, x); + }; + }; + } + return mix.apply(null, mixins)(instance); + }; +}; +``` + +The function in question is a simple wrapper around `pipe()`, a standard functional programming utility commonly [used to compose functions](https://medium.com/javascript-scene/master-the-javascript-interview-what-is-function-composition-20dfb109a1a0). A `pipe()` function exists in lodash as `lodash/flow`, in Ramda as `R.pipe()`, and even has its own operator in several functional programming languages. + +It should be familiar to everybody familiar with [functional programming](https://medium.com/javascript-scene/master-the-javascript-interview-what-is-functional-programming-7f218c68b3a0). As should its primary dependency: [Reduce](https://medium.com/javascript-scene/reduce-composing-software-fe22f0c39a1d). + +In this case, it’s being used to compose functional mixins, but that’s an irrelevant detail (and a whole other blog post). Here are the important details: + +The function takes any number of functional mixins and returns a function which applies them one after the other in a pipeline — like an assembly line. Each functional mixin takes the `instance` as an input, and tacks some stuff onto it before passing it on to the next function in the pipeline. + +If you omit the `instance`, a new object gets created for you. + +Sometimes we may want to compose the mixins differently. For example, you may want to pass `compose()` instead of `pipe()` to reverse the order of precedence. + +If you don’t need to customize the behavior, you simply leave the default alone, and get standard `pipe()` behavior. + +### Just the Facts ### + +Opinions about readability aside, here are **the objective facts** pertaining to this example: + +- I have multiple years’ experience with both ES5 and ES6 function expressions, arrows or otherwise. Familiarity bias is **not** a variable in this data. +- I wrote the ES6 version in a few seconds. It contained zero bugs (that I’m aware of — it passes all its unit tests). +- It took me several minutes to write the ES5 version. At least an order of magnitude more time. Minutes vs seconds. I lost my place in the function indentations twice. I wrote 3 bugs, all of which I had to debug and fix. Two of which I had to resort to `console.log()` to figure out what was going on. +- The ES6 version is 4 lines of code. +- The ES5 version is 21 lines long (17 actually contain code). +- In spite of its tedious verbosity, the ES5 version actually loses some of the information fidelity that is available in the ES6 version. It’s much longer, but **communicates less**, read on for details. +- The ES6 version contains 2 spreads for function parameters. The ES5 version omits the spreads, and instead uses the *implicit*`arguments` object, which hurts the readability of the function signature (fidelity downgrade 1). +- The ES6 version defines the default for `mix` in the function signature so you can clearly see that it’s a value for a parameter. The ES5 version obscures that detail and instead hides it deep inside the function body. (fidelity downgrade 2). +- The ES6 version has only 2 levels of indentation, which helps clarify the structure of how it should be read. The ES5 version has 6, and the nesting levels obscure rather than aid the readability of the function’s structure (fidelity downgrade 3). + +In the ES5 version, `pipe()` occupies most of the function body — so much so that it’s a bit **insane** to define it inline. It really **needs** to be broken out into a separate function to make the ES5 version readable: + +``` +var pipe = function () { + var fns = [].slice.call(arguments); + + return function (x) { + return fns.reduce(function (acc, fn) { + return fn(acc); + }, x); + }; +}; + +var composeMixins = function () { + var mixins = [].slice.call(arguments); + + return function (instance, mix) { + if (!instance) instance = {}; + if (!mix) mix = pipe; + + return mix.apply(null, mixins)(instance); + }; +}; +``` + +This seems clearly more readable and understandable to me. + +Let’s see what happens when we apply the same readability “optimization” to the ES6 version: + +``` +const pipe = (...fns) => x => fns.reduce((acc, fn) => fn(acc), x); + +const composeMixins = (...mixins) => ( + instance = {}, + mix = pipe +) => mix(...mixins)(instance); +``` + +Like the ES5 optimization, this version is more verbose (it adds a new variable that wasn’t there before). Unlike the ES5 version, this version is **not significantly more readable** after abstracting the definition of pipe. After all, it already had a variable name clearly assigned to it *in the function signature:*`mix`. + +The definition of `mix` was already contained on its own line, which makes it unlikely for readers to get confused about where it ends and the rest of the function continues. + +Now we have 2 variables representing the same thing instead of 1. Have we gained very much? Not obviously, no. + +So why is the ES5 version **obviously better** with the same function abstracted? + +Because the ES5 version is **obviously more complex.** The source of that complexity is the crux of this matter. I assert that the source of the complexity boils down to **syntax noise**, and that syntax noise is **obscuring the meaning of the function**, not helping. + +Let’s shift gears and eliminate some more variables. Let’s use ES6 for both examples, and only compare *arrow functions* vs *legacy function expressions:* + +``` +var composeMixins = function (...mixins) { + return function ( + instance = {}, + + mix = function (...fns) { + return function (x) { + return fns.reduce(function (acc, fn) { + return fn(acc); + }, x); + }; + } + ) { + return mix(...mixins)(instance); + }; +}; +``` + +This looks significantly more readable to me. All we’ve changed is that we’re taking advantage of **rest** and **default parameter** syntax. Of course, you’ll have to be **familiar** with rest and default syntax in order for this version to be more readable, but even if you’re not, I think it’s obvious that this version is still **less cluttered**. + +That helped a lot, but it’s still clear to me that this version is still cluttered enough that abstracting `pipe()` into its own function would **obviously help:** + +``` +const pipe = function (...fns) { + return function (x) { + return fns.reduce(function (acc, fn) { + return fn(acc); + }, x); + }; +}; + +// Legacy function expressions +const composeMixins = function (...mixins) { + return function ( + instance = {}, + mix = pipe + ) { + return mix(...mixins)(instance); + }; +}; +``` + +That’s better, right? Now that the `mix` assignment only occupies a single line, the structure of the function is much more clear — but there’s still too much syntax noise for my taste. In `composeMixins()`, it’s not clear to me at a glance where one function ends and another begins. + +Rather than call out function bodies, that `function` keyword seems to visually **blend in** with the identifiers around it. There are functions **hiding** in my function! Where does the parameter signature end and the function body begin? I can figure it out out if I look closely, but it’s not visually obvious to me. + +What if we could get rid of the function keyword, and call out return values by visually pointing to them with a big **fat arrow** `=>`instead of writing a `return` keyword that blends in with surrounding identifiers? + +Turns out, we can, and here’s what that looks like: + +``` +const composeMixins = (...mixins) => ( + instance = {}, + mix = pipe +) => mix(...mixins)(instance); +``` + +Now it should be clear what’s going on. `composeMixins()` is a function that takes any number of `mixins` and returns a function that takes two optional parameters, `instance`, and `mix`. It returns the result of piping `instance` through the composed `mixins`. + +Just one more thing… if we apply the same optimization to `pipe()`, it magically transforms into a one-liner: + +``` +const pipe = (...fns) => x => fns.reduce((acc, fn) => fn(acc), x); +``` + +With that definition on one-line, the advantage of abstracting it out into its own function is less clear. Remember, this function exists as a utility in Lodash, Ramda, and a bunch of other libraries, but is it really worth the overhead of importing another library? + +Is it even worth pulling it out into its own line? Probably. They’re really two different functions, and separating them makes that more clear. + +On the other hand, having it in-line clarifies type and usage expectations when you look at the parameter signature. Here’s what happens when we create it in-line: + +``` +const composeMixins = (...mixins) => ( + instance = {}, + mix = (...fns) => x => fns.reduce((acc, fn) => fn(acc), x) +) => mix(...mixins)(instance); +``` + +Now we’re back to the original function. Along the way, **we didn’t discard any meaning.** In fact, by declaring our parameters and default values inline, we **added information** about how the function is used, and what the values of the parameters might look like. + +All that extra code in the ES5 version was just noise. Syntax noise. It didn’t serve **any useful purpose** except to acclimate people who are **unfamiliar** with curried arrow functions. + +Once you have gained sufficient familiarity with curried arrow functions, it should be clear that the original version is **more readable** because there’s a lot less syntax to get lost in. + +It’s also **less error-prone**, because there’s a lot less surface area for bugs to hide in. + +I suspect there are lots of bugs hiding in legacy functions that would be found and eliminated if you were to upgrade to arrow functions. + +I also supect that your team would become significantly more productive if you learned to embrace and favor more of the concise syntax available in ES6. + +While it’s true that sometimes things are easier to understand if they’re made explicit, it’s also true that as a general rule, less code is better. + +If less code can accomplish the same thing and communicate more, without sacrificing any meaning, it’s **objectively** better. + +The key to knowing the difference is meaning. If more code fails to add more meaning, that code should not exist. That concept is so basic, it is a well-known style guideline for natural language. + +The same style guideline applies to source code. Embrace it, and your code will be better. + +At the end of the day, a light in the darkness. In response to yet another tweet saying the ES6 version less readable: + +[![Markdown](http://i2.muimg.com/1949/4287b75aa0b58a9d.png)](https://twitter.com/blakenewman) + +Time to get familiar with ES6, currying, and function composition. + +### Next Steps ### + +[“Learn JavaScript with Eric Elliott”](https://ericelliottjs.com/product/lifetime-access-pass/) members can watch the 55-minute [ES6 Curry & Composition](https://ericelliottjs.com/premium-content/es6-curry-composition/) lesson right now. + +If you’re not a member, you’re missing out! + +[![](https://cdn-images-1.medium.com/max/800/1*3njisYUeHOdyLCGZ8czt_w.jpeg)](https://ericelliottjs.com/product/lifetime-access-pass/) + +***Eric Elliott*** *is the author of [*“Programming JavaScript Applications”*](http://pjabook.com) (O’Reilly), and [*“Learn JavaScript with Eric Elliott”*](http://ericelliottjs.com/product/lifetime-access-pass/). He has contributed to software experiences for Adobe Systems, Zumba Fitness, The Wall Street Journal, ESPN, BBC, and top recording artists including Usher, Frank Ocean, Metallica, and many more.* + +*He spends most of his time in the San Francisco Bay Area with the most beautiful woman in the world.* + +--- + +> [掘金翻译计划](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)、[React](https://github.com/xitu/gold-miner#react)、[前端](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)。 From b682894aef71da63363fe56bd84cbd427d70eefe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=B9=E5=8F=B7=E4=B8=89?= Date: Tue, 25 Apr 2017 23:48:52 +0800 Subject: [PATCH 260/638] Fix a typo. --- ...e-programming-android-rxjava2-what-the-hell-is-this-part3.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO/functional-interfaces-functional-programming-and-lambda-expressions-reactive-programming-android-rxjava2-what-the-hell-is-this-part3.md b/TODO/functional-interfaces-functional-programming-and-lambda-expressions-reactive-programming-android-rxjava2-what-the-hell-is-this-part3.md index e625d1a042c..a548006095a 100644 --- a/TODO/functional-interfaces-functional-programming-and-lambda-expressions-reactive-programming-android-rxjava2-what-the-hell-is-this-part3.md +++ b/TODO/functional-interfaces-functional-programming-and-lambda-expressions-reactive-programming-android-rxjava2-what-the-hell-is-this-part3.md @@ -1,4 +1,4 @@ -> * 原文地址:[Reactive Programming [ Android RxJava2 ] ( What the hell is this ) Part3](https://www.teambition.com/project/583d8744180aa4d012496f03/tasks/scrum/583d8744fa1e93bf18a85a7a/task/58d0e5c09e4371c673330e96) +> * 原文地址:[Reactive Programming [ Android RxJava2 ] ( What the hell is this ) Part3](http://www.uwanttolearn.com/android/functional-interfaces-functional-programming-and-lambda-expressions-reactive-programming-android-rxjava2-what-the-hell-is-this-part3/) > * 原文作者:[Hafiz Waleed Hussain](http://www.uwanttolearn.com/author/admin/) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 译者: From d9368fb0528250c99c9a3f0cc6c11f1bc3f052c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=B9=E5=8F=B7=E4=B8=89?= Date: Tue, 25 Apr 2017 23:54:44 +0800 Subject: [PATCH 261/638] Create testing-mvp-using-espresso-and-mockito.md --- .../testing-mvp-using-espresso-and-mockito.md | 498 ++++++++++++++++++ 1 file changed, 498 insertions(+) create mode 100644 TODO/testing-mvp-using-espresso-and-mockito.md diff --git a/TODO/testing-mvp-using-espresso-and-mockito.md b/TODO/testing-mvp-using-espresso-and-mockito.md new file mode 100644 index 00000000000..b31ff8af249 --- /dev/null +++ b/TODO/testing-mvp-using-espresso-and-mockito.md @@ -0,0 +1,498 @@ +> * 原文地址:[TESTING MVP USING ESPRESSO AND MOCKITO](https://josiassena.com/testing-mvp-using-espresso-and-mockito/) +> * 原文作者:[Josias Sena](https://josiassena.com/about-me/) +> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) +> * 译者: +> * 校对者: + +# Testing MVP using Espresso and Mockito # + +As software developers, we try our best to do what is right and make sure that we are not incompetent, and try to have others and our employers trust in the code we write. We all try to follow best practices and apply good architecture patterns, but sometimes many of us find it difficult to actually test what we code. + +Personally, I have seen a few open-source projects where the developers are great at building awesome products—and can build any application you can think of—but for some reason lack at writing the proper tests, if any at all. + +This here is another simplified tutorial on how to unit test the “oh so amazing” MVP architecture pattern that many of us try to follow. + +Before I continue, I want to mention that I assume you are familiar with the MVP pattern, and have used it before. I will not go over the MVP pattern at all, and I will not explain how it works. With that in mind, I’d like to mention ahead of time that I am using one of my favorite libraries for MVPs created by a guy named [Hannes Dorfman](http://hannesdorfmann.com/) called [Mosby](https://github.com/sockeqwe/mosby). For simplicity’s sake, I am also using the view binding library called [ButterKnife](http://jakewharton.github.io/butterknife/). + +So what is this application we will be looking at? + +It is a very simple Android app, and it does one thing and one thing only: It hides and displays a TextView with the click of a button. That’s it. + +Here’s how the app looks initially: + +![Initial](https://i1.wp.com/www.andevcon.com/hubfs/EVENTS_ASSETS/ANDEVCON/Images/Article_Images/MVP%20Mockito/IVvsdac.png) + +Here’s how it looks when the button is clicked: + +![724E8fE.png](https://i2.wp.com/www.andevcon.com/hubfs/EVENTS_ASSETS/ANDEVCON/Images/Article_Images/MVP%20Mockito/724E8fE.png) + +For the sake of this article, let’s imagine that this is a multi-million-dollar product, and that the way it is now is the way it should be for a very long time. And if it were to ever change, we should be notified immediately. + +So we have three things in this app: a blue toolbar with the app name, a text view that displays “Hello World,” and a button that hides/shows the TextView. + +Before I start, I would like to mention that you can find all of the code for this article on [my GitHub](https://github.com/josias1991/TestingMVP); if you do not want to read any of my rambling, please feel free to skip the rest of the post and go straight to the code. Comments will be added for clarity. + +Now, lets get testing! + +## **The Espresso tests** ## + +The first thing we want to test is our awesome ToolBar design. I mean, it is a million-dollar app after all; we need to make sure it stays that way! + +So first, here’s the complete code used to test the TooBar design. If you have no idea what is going on here, don’t worry: We will walk through it together. + +``` +@RunWith (AndroidJUnit4.class) +public class MainActivityTest { + + @Rule + public ActivityTestRule activityTestRule = + new ActivityTestRule<>(MainActivity.class); + + @Test + public void testToolbarDesign() { + onView(withId(R.id.toolbar)).check(matches(isDisplayed())); + + onView(withText(R.string.app_name)).check(matches(withParent(withId(R.id.toolbar)))); + + onView(withId(R.id.toolbar)).check(matches(withToolbarBackGroundColor())); + } + + private Matcher<? super View> withToolbarBackGroundColor() { + return new BoundedMatcher<View, View>(View.class) { + @Override + public boolean matchesSafely(View view) { + final ColorDrawable buttonColor = (ColorDrawable) view.getBackground(); + + return ContextCompat + .getColor(activityTestRule.getActivity(), R.color.colorPrimary) == + buttonColor.getColor(); + } + + @Override + public void describeTo(Description description) { + } + }; + } +} +``` + +First things first: We need to tell JUnit what sort of test we are running. This is what the first line does (@RunWith (AndroidJUnit4.class)). It says. “Hey, listen, I want to run an Android test that uses JUnit4 on an actual connected device.” + +So what exactly is an Android test? An Android test is a test that runs on the device instead of locally on the [Java Virtual Machine (JVM)](https://en.wikipedia.org/wiki/Java_virtual_machine) on your computer. This means that a device needs to be connected to your computer in order to run the test. This gives the test code access to functional Android framework APIs. + +These tests go in the androidTest directory. + +![android_test_directory](https://i0.wp.com/www.andevcon.com/hs-fs/hubfs/EVENTS_ASSETS/ANDEVCON/Images/Article_Images/MVP%20Mockito/gcpEaEX.png?w=442) + +Next lets take a look at this thing called an “ActivityTestRule”. Since the android documentation explains it really well, here it is: + +*“This rule provides functional testing of a single activity. The activity under test will be launched before each test annotated with [Test](http://junit.org/javadoc/latest/org/junit/Test.html) and before methods annotated with [Before](http://junit.sourceforge.net/javadoc/org/junit/Before.html). It will be terminated after the test is completed and methods annotated with [After](http://junit.sourceforge.net/javadoc/org/junit/After.html) are finished. During the duration of the test you will be able to manipulate your Activity directly.”* + +This basically says, “This is the activity I want to run my test.” + +Lets get in to the testToolbarDesign() method and see what the hell is going on. + +### **Testing the toolbar** ### + +``` onView(withId(R.id.toolbar)).check(matches(isDisplayed())); +``` + +What this test does is find a view with the ID that matches “R.id.toolbar,” then checks to make sure that this view is visible/displayed. If this line were to fail, the test would end right there and wouldn’t even bother with the rest. + +``` onView(withText(R.string.app_name)).check(matches(withParent(withId(R.id.toolbar)))); +``` + +This one says “Hey, lets see if there is some text that equals ‘R.string.app_name’ and has a parent whose id is R.id.toolbar.” + +The last line in this test is a bit more involved. So basically what it is trying to do is to make sure that the background of the toolbar is equal to the app’s primary color. + +``` +onView(withId(R.id.toolbar)).check(matches(withToolbarBackGroundColor())); +``` + +So by default, Espresso does not provide a straightforward way to do this, so we need to create what is called a [Matcher](https://developer.android.com/reference/android/support/test/espresso/matcher/package-summary.html). A Matcher is exactly what we have been using previously to match some view property to another. In this case, we want to match the primary color to the toolbars background. + +What we do is we create a [Matcher](https://developer.android.com/reference/android/support/test/espresso/matcher/BoundedMatcher.html) and override the matchesSafely() method. The code inside this method is pretty easy to understand. First we get the toolbar’s background, then we compare it to the app’s primary color. If it is equal, it returns true; false otherwise. + +### **Test TextView hides/shows properly** ### + +Ok, before I show any code, I just want to mention that this code is a bit more verbose, but pretty straightforward to read. I have added some comments to show what exactly is going on. + +``` + +@RunWith (AndroidJUnit4.class) +public class MainActivityTest { + + @Rule + public ActivityTestRule activityTestRule = + new ActivityTestRule<>(MainActivity.class); + + // ... + + @Test + public void testHideShowTextView() { + + // Check the TextView is displayed with the right text + onView(withId(R.id.tv_to_show_hide)).check(matches(isDisplayed())); + onView(withId(R.id.tv_to_show_hide)).check(matches(withText("Hello World!"))); + + // Check the button is displayed with the right initial text + onView(withId(R.id.btn_change_visibility)).check(matches(isDisplayed())); + onView(withId(R.id.btn_change_visibility)).check(matches(withText("Hide"))); + + // Click on the button + onView(withId(R.id.btn_change_visibility)).perform(click()); + + // Check that the TextView is now hidden + onView(withId(R.id.tv_to_show_hide)).check(matches(not(isDisplayed()))); + + // Check that the button has the proper text + onView(withId(R.id.btn_change_visibility)).check(matches(withText("Show"))); + + // Click on the button + onView(withId(R.id.btn_change_visibility)).perform(click()); + + // Check the TextView is displayed again with the right text + onView(withId(R.id.tv_to_show_hide)).check(matches(isDisplayed())); + onView(withId(R.id.tv_to_show_hide)).check(matches(withText("Hello World!"))); + + // Check that the button has the proper text + onView(withId(R.id.btn_change_visibility)).check(matches(isDisplayed())); + onView(withId(R.id.btn_change_visibility)).check(matches(withText("Hide"))); + } + + // ... +} +``` + +The gist of this code is that it is making sure that when the app opens, the TextView with ID “R.id.tv_to_show_hide” is displayed, and the text displayed on the TextView says “Hello World!” + +Then we check that the button is also displayed properly, and that the text on the button (by default) is set to “Hide”. + +Next we click on the button. A click on a button is very straightforward, and it is very easy to read how it is done. This time instead of calling “.check” after we find a view by ID, we call .perform(), and inside the perform() method, we pass in click(). The perform() method says “Please do the following action,” and then “performs” whatever action is passed in. In our case it is a click() action. + +Since the “Hide” button was clicked, we need to make sure the TextView is actually hidden now. We do this by adding a “not()” in front of the isDisplayed() method we used previously, and the button text has been changed to “Show”. This is the same as doing “!=” in plain ol’ java. + +``` + + +@RunWith (AndroidJUnit4.class) +public class MainActivityTest { + // ... + + @Test + public void testHideShowTextView() { + + // ... + + // Check that the TextView is now hidden + onView(withId(R.id.tv_to_show_hide)).check(matches(not(isDisplayed()))); + + // Check that the button has the proper text + onView(withId(R.id.btn_change_visibility)).check(matches(withText("Show"))); + + // ... + } + + // ... +} +``` + +The code after these lines is the reverse of what we did before. We click the button again, make sure the TextView is visible again, and make sure that the button text has changed to successfully to match the situation. + +And that’s it! + +Here is the full UI test code: + +``` + +@RunWith (AndroidJUnit4.class) +public class MainActivityTest { + + @Rule + public ActivityTestRule activityTestRule = + new ActivityTestRule<>(MainActivity.class); + + @Test + public void testToolbarDesign() { + onView(withId(R.id.toolbar)).check(matches(isDisplayed())); + + onView(withText(R.string.app_name)).check(matches(withParent(withId(R.id.toolbar)))); + + onView(withId(R.id.toolbar)).check(matches(withToolbarBackGroundColor())); + } + + @Test + public void testHideShowTextView() { + + // Check the TextView is displayed with the right text + onView(withId(R.id.tv_to_show_hide)).check(matches(isDisplayed())); + onView(withId(R.id.tv_to_show_hide)).check(matches(withText("Hello World!"))); + + // Check the button is displayed with the right initial text + onView(withId(R.id.btn_change_visibility)).check(matches(isDisplayed())); + onView(withId(R.id.btn_change_visibility)).check(matches(withText("Hide"))); + + // Click on the button + onView(withId(R.id.btn_change_visibility)).perform(click()); + + // Check that the TextView is now hidden + onView(withId(R.id.tv_to_show_hide)).check(matches(not(isDisplayed()))); + + // Check that the button has the proper text + onView(withId(R.id.btn_change_visibility)).check(matches(withText("Show"))); + + // Click on the button + onView(withId(R.id.btn_change_visibility)).perform(click()); + + // Check the TextView is displayed again with the right text + onView(withId(R.id.tv_to_show_hide)).check(matches(isDisplayed())); + onView(withId(R.id.tv_to_show_hide)).check(matches(withText("Hello World!"))); + + // Check that the button has the proper text + onView(withId(R.id.btn_change_visibility)).check(matches(isDisplayed())); + onView(withId(R.id.btn_change_visibility)).check(matches(withText("Hide"))); + } + + private Matcher<? super View> withToolbarBackGroundColor() { + return new BoundedMatcher<View, View>(View.class) { + @Override + public boolean matchesSafely(View view) { + final ColorDrawable buttonColor = (ColorDrawable) view.getBackground(); + + return ContextCompat + .getColor(activityTestRule.getActivity(), R.color.colorPrimary) == + buttonColor.getColor(); + } + + @Override + public void describeTo(Description description) { + } + }; + } +} +``` + +## **The Unit Tests** ## + +The great thing about unit tests—as opposed to Android tests—is that they run locally on your machine on the JVM. No need to have a device attached, and the tests run so much faster. The downside is that they do not have access to functional Android framework APIs. Overall, when testing anything other then UI you should try your best to write unit tests instead of Android/Instrumentation tests. The faster the tests, the better. + +Lets start with the directory of the unit tests. The unit tests go in a different location then the Android tests did. + +![different_location](https://i1.wp.com/www.andevcon.com/hubfs/EVENTS_ASSETS/ANDEVCON/Images/Article_Images/MVP%20Mockito/mYBjN1x.png) + +Before moving on lets take a look at our presenter and what we are going to consider our model for this tutorial. + +### *Lets start with the presenter* ### + +``` + +public class MainPresenterImpl extends MvpBasePresenter implements MainPresenter { + + @Override + public void reverseViewVisibility(final View view) { + if (view != null) { + if (view.isShown()) { + Utils.hideView(view); + + setButtonText("Show"); + } else { + Utils.showView(view); + + setButtonText("Hide"); + } + } + } + + private void setButtonText(final String text) { + if (isViewAttached()) { + getView().setButtonText(text); + } + } +} +``` + +Simple enough. We have two methods: One checks to see if the view is visible. If it is, hide it, otherwise show it. Once this is done, we call into our view and say “Hey, change the button text to either ‘Hide’ or ‘Show’.” + +Our reverseViewVisibility() method calls into what we call (for this tutorial at least) our “model” to set the proper visibility on the view passed in. + +### *Lets take a look at our model.* ### + +``` +public final class Utils { + + // ... + + public static void showView(View view) { + if (view != null) { + view.setVisibility(View.VISIBLE); + } + } + + public static void hideView(View view) { + if (view != null) { + view.setVisibility(View.GONE); + } + } +``` + +There are two methods: showView(View) and hideView(View). What these methods do is very straightforward and self-explanatory. We check if the view is null; if not, we either hide it or show it. + +Great, now that we are familiar with both our presenter and our “model,” lets go ahead and test them. After all, this is a multi-million-dollar product, and we cannot have anything go wrong. + +Let start start testing our presenter first. When it comes to a presenter—ANY presenter—we need to make sure that the view is attached to the presenter. Note: We are NOT testing the view. We just need to make sure that the view is attached so that we can verify the proper view methods are being made in the right time. Keep this in mind as this is very important. + +We will be using Mockito to run our tests, so just like we did with our unit tests, we ned to tell Android, “Hey, we want to run these tests with the MockitoJUnitRunner.” To do so we add the @RunWith (MockitoJUnitRunner.class) annotation on top of our test class. + +So we know two things right from the start: We need a mocked View because our presenter uses a View Object to hide or show it, and we also need our presenter. + +This is how we mock using Mockito + +``` +@RunWith (MockitoJUnitRunner.class) +public class MainPresenterImplTest { + + MainPresenterImpl presenter; + + @Before + public void setUp() throws Exception { + presenter = new MainPResenterImpl(); + presenter.attachView(Mockito.mock(MainView)); + } + + // ... +} +``` + +The first actual test we want to write is called “testReverseViewVisibilityFromVisibleToGone”. As the name says, we are going to make sure that the presenter sets the proper visibility when the view passed in to the reverseViewVisibility() method is visible. + +``` + @Test + public void testReverseViewVisibilityFromVisibleToGone() throws Exception { + final View view = Mockito.mock(View.class); + when(view.isShown()).thenReturn(true); + + presenter.reverseViewVisibility(view); + + Mockito.verify(view, Mockito.atLeastOnce()).setVisibility(View.GONE); + Mockito.verify(presenter.getView(), Mockito.atLeastOnce()).setButtonText(anyString()); + } +``` + +Let’s step through this together. What is actually going on here? Well, when the isShown() method is called on the view that is passed in to the presenter, we want to say yes, the view is shown, because we are testing from visible to gone, so we need to start at visible. Then, we call the presenters reverseViewVisibility() method by passing in the mocked view. Now we need to verify that the mocked views .setVisibility() was called at least once, and it was set to View.GONE. Afterwards, we need to verify that the presenters view setButtonText() method was called. Not that hard right? + +Alright, lets do the opposite. Before moving on and looking at the next piece of code, take some time to figure it out yourself. How would we test going from hidden to visible? Think about what you already know. + +Here’s the code: + +``` + @Test + public void testReverseViewVisibilityFromGoneToVisible() throws Exception { + final View view = Mockito.mock(View.class); + when(view.isShown()).thenReturn(false); + + presenter.reverseViewVisibility(view); + + Mockito.verify(view, Mockito.atLeastOnce()).setVisibility(View.VISIBLE); + Mockito.verify(presenter.getView(), Mockito.atLeastOnce()).setButtonText(anyString()); + } +``` + + +Lets move on to testing our “Model.” As before, we start by adding the @RunWith (MockitoJUnitRunner.class) annotation on top of our class. + +``` +@RunWith(MockitoJUnitRunner.class) + +publicclassUtilsTest{ + + // ... + +} +``` + + +As mentioned previously, our Utils class first checks if the view is not null. If not, a visibility is applied, otherwise nothing is done. + +The tests for this class are very easy, so I am just going to put the whole thing here without stepping through it line by line. + +``` +@RunWith (MockitoJUnitRunner.class) +public class UtilsTest { + + @Test + public void testShowView() throws Exception { + final View view = Mockito.mock(View.class); + + Utils.showView(view); + + Mockito.verify(view).setVisibility(View.VISIBLE); + } + + @Test + public void testHideView() throws Exception { + final View view = Mockito.mock(View.class); + + Utils.hideView(view); + + Mockito.verify(view).setVisibility(View.GONE); + } + + @Test + public void testShowViewWithNullView() throws Exception { + Utils.showView(null); + } + + @Test + public void testHideViewWithNullView() throws Exception { + Utils.hideView(null); + } +} +``` + + +I’ll explain what is going on in the testShowViewWithNullView() and testHideViewWithNullView() methods. Why would we want to test something like this? Well if we think about it for a second, we do not want our method calls to crash the entire application because the view was null. + +Lets take a look at the Utils showView() method. If we remove the null check, the app will throw a NullPointerException and would crash. + +``` +public final class Utils { + + // ... + + public static void showView(View view) { + if (view != null) { + view.setVisibility(View.VISIBLE); + } + } + + // ... +} +``` + +In other scenarios we may want the app to actually throw an exception. How would we test an exception? Its actually very easy: You would just pass in an expected param to the @Test annotation like so: + +``` +@RunWith (MockitoJUnitRunner.class) +public class UtilsTest { + + // ... + + @Test (expected = NullPointerException.class) + public void testShowViewWithNullView() throws Exception { + Utils.showView(null); + } +} +``` + +If no exception is thrown, the test would fail. + +Again, you can find all of the code on [GitHub](https://github.com/josias1991/TestingMVP). + +Now that we are at the end of the post, I want to mention something to you guys: Testing will not always be as straightforward as it was in this example, but it doesn’t mean it can’t, and shouldn’t be done. As software developers, we need to make sure that our applications work properly and as expected. We need to make sure others trust our code. I have been doing this for many years and you couldn’t imagine how many times tests have saved me, even for the simplest thing such as changing a view ID. + +No one is perfect, but tests help us get a step closer. Keep on coding, keep on testing and until next time! + +--- + +> [掘金翻译计划](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)、[React](https://github.com/xitu/gold-miner#react)、[前端](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)。 From ce2c97cc9c7b17f0480ab446af0d86fff6d76794 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=B9=E5=8F=B7=E4=B8=89?= Date: Wed, 26 Apr 2017 00:01:41 +0800 Subject: [PATCH 262/638] Update familiarity-bias-is-holding-you-back-its-time-to-embrace-arrow-functions.md --- ...s-is-holding-you-back-its-time-to-embrace-arrow-functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO/familiarity-bias-is-holding-you-back-its-time-to-embrace-arrow-functions.md b/TODO/familiarity-bias-is-holding-you-back-its-time-to-embrace-arrow-functions.md index 6e84b0f8352..448af4f0d5a 100644 --- a/TODO/familiarity-bias-is-holding-you-back-its-time-to-embrace-arrow-functions.md +++ b/TODO/familiarity-bias-is-holding-you-back-its-time-to-embrace-arrow-functions.md @@ -1,5 +1,5 @@ > * 原文地址:[Familiarity Bias is Holding You Back: It’s Time to Embrace Arrow Functions](https://medium.com/javascript-scene/familiarity-bias-is-holding-you-back-its-time-to-embrace-arrow-functions-3d37e1a9bb75) -> * 原文作者:[Eric Elliott](https://medium.com/@_ericelliott?source=post_header_lockup) +> * 原文作者:[Eric Elliott](https://medium.com/@_ericelliott) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 译者: > * 校对者: From b1af3e779e5fdb72a2f854941c3e51255796c73f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=B9=E5=8F=B7=E4=B8=89?= Date: Wed, 26 Apr 2017 00:02:17 +0800 Subject: [PATCH 263/638] Update From bb62e86ca560f3c1b0180379e87cd3c9527c811e Mon Sep 17 00:00:00 2001 From: sqrtthree Date: Wed, 26 Apr 2017 10:43:54 +0800 Subject: [PATCH 264/638] fix typos --- TODO/a-complete-guide-to-learn-rxjava.md | 100 ----------------------- TODO/design-principle-aesthetics.md | 2 +- TODO/yeah-redesign-part-1.md | 4 +- 3 files changed, 3 insertions(+), 103 deletions(-) delete mode 100644 TODO/a-complete-guide-to-learn-rxjava.md diff --git a/TODO/a-complete-guide-to-learn-rxjava.md b/TODO/a-complete-guide-to-learn-rxjava.md deleted file mode 100644 index bd05349652f..00000000000 --- a/TODO/a-complete-guide-to-learn-rxjava.md +++ /dev/null @@ -1,100 +0,0 @@ -> * 原文地址:[A Complete Guide To Learn RxJava](https://blog.mindorks.com/a-complete-guide-to-learn-rxjava-b55c0cea3631) -> * 原文作者:[Amit Shekhar](https://blog.mindorks.com/@amitshekhar?source=post_header_lockup) -> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 译者: -> * 校对者: - -# **A Complete Guide To Learn RxJava** # - -![](https://cdn-images-1.medium.com/max/800/1*S_Mq74exiad5JrgyN-41pw.png) - -### **A Complete Resources To Learn RxJava At One Place.** ### - -#### What is RxJava? #### - -> RxJava is used for reactive programming. In reactive programming, the consumer reacts to the data as it comes in. Reactive programming allows for event changes to propagate to registered observers. - -As we know that the RxJava is the most important library for Android Projects. So we must have a proper grip on using RxJava in the Android Projects. - -#### **And I believe:** #### - -> **RxJava is an art and endless possibilities await those who can master it** - -### Let’s see all the best resources available to learn RxJava in the best possible way. ### - -### [Grokking RxJava Series](http://blog.danlew.net/2014/09/15/grokking-rxjava-part-1/) ### - -[RxJava](https://github.com/ReactiveX/RxJava) is the new hotness amongst Android developers these days. The only problem is that it can be difficult to approach initially. Functional Reactive Programming is hard when you come from an imperative world, but once you understand it, it’s so awesome. - -### [Learn RxJava By Examples](https://github.com/amitshekhariitbhu/RxJava2-Android-Samples) ### - -**Because Learning by examples is the best way to learn**. - -[![](http://i1.piimg.com/1949/08be9bc226fd2cb4.png)](https://github.com/amitshekhariitbhu/RxJava2-Android-Samples) - -It consists of many examples like: - -- How to use operators in RxJava? -- How to do networking in RxJava? -- How to implement RxBus(EventBus) in RxJava? -- How to implement pagination with RxJava? - -Another one is from Kaushik. I have learned a lot from here. - -[![Markdown](http://i1.piimg.com/1949/34e05ecb9f101fa6.png)](https://github.com/kaushikgopal/RxJava-Android-Samples) - -### [Exploring RxJava 2 for Android by Jake Wharton](https://www.youtube.com/watch?v=htIXKI5gOQU) ### - -![](https://cdn.embedly.com/widgets/xcomm.html#name=media&sid=fb6eccf04e144d928ac51cf751abf7b0) - -### [Managing State with RxJava by Jake Wharton](https://www.youtube.com/watch?v=0IKHxjkgop4) ### - -![](https://cdn.embedly.com/widgets/xcomm.html#name=media&sid=7082ade718c745b3b8233afc1662d939) - -### [Another Learn Rxjava By Examples](https://www.youtube.com/watch?v=k3D0cWyNno4) ### - -![](https://cdn.embedly.com/widgets/xcomm.html#name=media&sid=06f58fec20af46309597cec9b86abfff) - -### [Understanding RxJava Subject — Publish, Replay, Behavior and Async Subject](https://blog.mindorks.com/understanding-rxjava-subject-publish-replay-behavior-and-async-subject-224d663d452f) ### - -[![](http://i1.piimg.com/1949/b30bdcbf45a1753c.png)](https://blog.mindorks.com/understanding-rxjava-subject-publish-replay-behavior-and-async-subject-224d663d452f) - -### [Using RxJava In Networking](https://blog.mindorks.com/rxjava-fast-android-networking-6e3d90ee4387) ### - -[![Markdown](http://i1.piimg.com/1949/1120535cb191dff0.png)](https://blog.mindorks.com/rxjava-fast-android-networking-6e3d90ee4387) - -### [Better Explanation of RxJava and Using It In Android](https://medium.com/@kurtisnusbaum/rxandroid-basics-part-1-c0d5edcf6850#.z6ha43z02) ### - -[![Markdown](http://i1.piimg.com/1949/b12f79e5f3711746.png)](https://medium.com/@kurtisnusbaum/rxandroid-basics-part-1-c0d5edcf6850) - -### [The Complete Introduction To Reactive Programming](https://gist.github.com/staltz/868e7e9bc2a7b8c1f754) ### - -[Link here.](https://gist.github.com/staltz/868e7e9bc2a7b8c1f754) - -### [**Intro To RxJava**](https://github.com/Froussios/Intro-To-RxJava) ### - -[Link here.](https://github.com/Froussios/Intro-To-RxJava) - -### [Crunching RxAndroid](https://medium.com/crunching-rxandroid/crunching-rxandroid-intro-c27eb6f009ea) ### - -[![Markdown](http://i1.piimg.com/1949/f6c9ba29bdef291f.png)](https://medium.com/crunching-rxandroid/crunching-rxandroid-intro-c27eb6f009ea) - -### [Why should you use RxJava in Android?](http://blog.feedpresso.com/2016/01/25/why-you-should-use-rxjava-in-android-a-short-introduction-to-rxjava.html) ### - -[![Markdown](http://i1.piimg.com/1949/d6af73998d20e852.png)](http://blog.feedpresso.com/2016/01/25/why-you-should-use-rxjava-in-android-a-short-introduction-to-rxjava.html) - -> **With great power, comes great responsibility. So learn it in the best way.** - -Happy Coding :) - -**Thanks for reading this article. Be sure to click ❤ below to recommend this article if you found it helpful. It means a lot to me.** - -***For more about programming, follow me and [Mindorks](https://blog.mindorks.com), so you’ll get notified when we write new posts.*** - -[**Join The Mindorks Community And Learn From Each Other.**](https://mindorks.com/join-community) - -**Also, Let’s become friends on [Twitter](https://twitter.com/amitiitbhu) , [Linkedin](https://in.linkedin.com/in/amit-shekhar-3b556499) , [Github](https://github.com/amitshekhariitbhu) and [Facebook](https://www.facebook.com/amit.shekhar.iitbhu).** - ---- - -> [掘金翻译计划](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)、[React](https://github.com/xitu/gold-miner#react)、[前端](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)。 diff --git a/TODO/design-principle-aesthetics.md b/TODO/design-principle-aesthetics.md index 62107e53f0e..9a1f1a23daf 100644 --- a/TODO/design-principle-aesthetics.md +++ b/TODO/design-principle-aesthetics.md @@ -1,5 +1,5 @@ > * 原文地址:[Design principle: Aesthetics](https://uxdesign.cc/design-principle-aesthetics-af926f8f86fe) -> * 原文作者:[Anton Nikolov](https://uxdesign.cc/@antonnikolov?source=post_header_lockup) +> * 原文作者:[Anton Nikolov](https://uxdesign.cc/@antonnikolov) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 译者: > * 校对者: diff --git a/TODO/yeah-redesign-part-1.md b/TODO/yeah-redesign-part-1.md index 5c105a26aaf..7828afc10d1 100644 --- a/TODO/yeah-redesign-part-1.md +++ b/TODO/yeah-redesign-part-1.md @@ -1,5 +1,5 @@ > * 原文地址:[Yeah, redesign(Part 1)](https://medium.muz.li/yeah-redesign-part-1-b61af07eb41a) -> * 原文作者:[Jingxi Li](https://medium.muz.li/@jingxili?source=post_header_lockup) +> * 原文作者:[Jingxi Li](https://medium.muz.li/@jingxili) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 译者: > * 校对者: @@ -111,7 +111,7 @@ It is designers’ responsibility to do the design QA, and ensure the design was By the time we started to think redesign, Sing had moved from US centric app to an international product. To serve the community of global users, we localized Sing! from 12 languages to 20 languages. When applying foreign language into an English-based app, the UI could easily break. For example, compared to English, German or Russian takes more characters to express the same meanings. A defined limited space that would normally fit English labels would not work for German and Russian. Without a clear rule on how to set spacing and apply the right hierarchy, our localized languages were either cut off or presented in the smaller sizes. Addressing these issues for each language one by one consumed a lot of efforts from our engineers and QA. We know through the redesign, we need to find a sustainable solution that could optimize for all the different languages that we have or might have in the future. -![](https://cdn-images-1.medium.com/max/2000/1*cAPZbmDZEe5byrPDOB7QVA.jpeg) +![](https://cdn-images-1.medium.com/max/2000/1*cAPZbmDZEe5byrPDOB7QVA.jpeg) source:shutterstock From 10c3020a2d4daea8b89e7a36c4218725ad17923c Mon Sep 17 00:00:00 2001 From: ylq167 Date: Wed, 26 Apr 2017 19:42:19 +0800 Subject: [PATCH 265/638] =?UTF-8?q?=E2=80=98=E4=BF=AE=E6=94=B9=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO/on-loser-experience-design.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO/on-loser-experience-design.md b/TODO/on-loser-experience-design.md index 8af076f4589..aaf85983f47 100644 --- a/TODO/on-loser-experience-design.md +++ b/TODO/on-loser-experience-design.md @@ -6,7 +6,7 @@ # 针对失败者的用户体验设计 # -## 设计平台或产品不仅是针对赢家,大V和意见领袖。## +## 设计平台或产品不仅是针对赢家,大V和意见领袖。 ## ![](https://cdn-images-1.medium.com/max/800/1*kVOEiUv3YK8tcYEa5QKmLA.jpeg) From ea8274aa9479deacca75a73e55ec2dd5dde00aab Mon Sep 17 00:00:00 2001 From: mnikn <329121948@qq.com> Date: Wed, 26 Apr 2017 20:23:06 +0800 Subject: [PATCH 266/638] modify v3 --- TODO/node-js-war-stories-solving-issues-in-production.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TODO/node-js-war-stories-solving-issues-in-production.md b/TODO/node-js-war-stories-solving-issues-in-production.md index 5568c9ce76a..d701e0c88e4 100644 --- a/TODO/node-js-war-stories-solving-issues-in-production.md +++ b/TODO/node-js-war-stories-solving-issues-in-production.md @@ -76,7 +76,7 @@ Netflix 的底层代码包含了每 6 分钟运行的定时代码,从拓展资 作为一个在 PaaS 运行的 早期 Trace 版本,它通过公共云来与我们的其他服务通信。 -为了确保我们的请求是完整的,我们决定对所有请求进行签名。为了实现这个,我们看了 Joyent 的 [HTTP signing library](https://github.com/joyent/node-http-signature)。很棒的是,[request](https://www.npmjs.com/package/request) 这一模块支持开箱即用的Http签名。 +为了确保我们的请求是完整的,我们决定对所有请求进行签名。为了实现这个,我们看了 Joyent 的 [HTTP signing library](https://github.com/joyent/node-http-signature)。很棒的是,[request](https://www.npmjs.com/package/request) 这一模块支持开箱即用的HTTP签名。 **解决方案代价不仅很大,而且会对我们的响应速度造成不好的影响。** @@ -106,7 +106,7 @@ Netflix 的底层代码包含了每 6 分钟运行的定时代码,从拓展资 ## nearForm: 不要堵塞 Node.js 的事件循环 ## -**React 现在很流行。**开发者在前端和后端都会使用它,甚至他们更进一步用它来构建同构的 JavaScript 应用。 +** React 现在很流行。**开发者在前端和后端都会使用它,甚至他们更进一步用它来构建同构的 JavaScript 应用。 > 然而,渲染 React 页面会让 CPU 有挺大的负担,当绘制复杂的 React 内容时会受到 CPU 限制。 From a2c3a3163a830c3b411f0f435ab172e0f21ad118 Mon Sep 17 00:00:00 2001 From: mnikn <329121948@qq.com> Date: Wed, 26 Apr 2017 20:25:14 +0800 Subject: [PATCH 267/638] modify --- TODO/node-js-war-stories-solving-issues-in-production.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO/node-js-war-stories-solving-issues-in-production.md b/TODO/node-js-war-stories-solving-issues-in-production.md index d701e0c88e4..48fc8b963ff 100644 --- a/TODO/node-js-war-stories-solving-issues-in-production.md +++ b/TODO/node-js-war-stories-solving-issues-in-production.md @@ -106,7 +106,7 @@ Netflix 的底层代码包含了每 6 分钟运行的定时代码,从拓展资 ## nearForm: 不要堵塞 Node.js 的事件循环 ## -** React 现在很流行。**开发者在前端和后端都会使用它,甚至他们更进一步用它来构建同构的 JavaScript 应用。 +**React 现在很流行。 **开发者在前端和后端都会使用它,甚至他们更进一步用它来构建同构的 JavaScript 应用。 > 然而,渲染 React 页面会让 CPU 有挺大的负担,当绘制复杂的 React 内容时会受到 CPU 限制。 From d0865effc554de20b50e9b0de840d7b3bb76066a Mon Sep 17 00:00:00 2001 From: mnikn <329121948@qq.com> Date: Wed, 26 Apr 2017 20:28:08 +0800 Subject: [PATCH 268/638] fix bold --- TODO/node-js-war-stories-solving-issues-in-production.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO/node-js-war-stories-solving-issues-in-production.md b/TODO/node-js-war-stories-solving-issues-in-production.md index 48fc8b963ff..598a84f10d9 100644 --- a/TODO/node-js-war-stories-solving-issues-in-production.md +++ b/TODO/node-js-war-stories-solving-issues-in-production.md @@ -106,7 +106,7 @@ Netflix 的底层代码包含了每 6 分钟运行的定时代码,从拓展资 ## nearForm: 不要堵塞 Node.js 的事件循环 ## -**React 现在很流行。 **开发者在前端和后端都会使用它,甚至他们更进一步用它来构建同构的 JavaScript 应用。 +**React 现在很流行**。开发者在前端和后端都会使用它,甚至他们更进一步用它来构建同构的 JavaScript 应用。 > 然而,渲染 React 页面会让 CPU 有挺大的负担,当绘制复杂的 React 内容时会受到 CPU 限制。 From 340158da7db7145b1c1b749c232105ae2990693d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=B9=E5=8F=B7=E4=B8=89?= Date: Wed, 26 Apr 2017 22:31:08 +0800 Subject: [PATCH 269/638] Create the-truth-is-in-the-code.md --- TODO/the-truth-is-in-the-code.md | 272 +++++++++++++++++++++++++++++++ 1 file changed, 272 insertions(+) create mode 100644 TODO/the-truth-is-in-the-code.md diff --git a/TODO/the-truth-is-in-the-code.md b/TODO/the-truth-is-in-the-code.md new file mode 100644 index 00000000000..8e042fa1d9d --- /dev/null +++ b/TODO/the-truth-is-in-the-code.md @@ -0,0 +1,272 @@ +> * 原文地址:[The truth is in the code](https://medium.freecodecamp.com/the-truth-is-in-the-code-86a712362c99) +> * 原文作者:[Bertil Muth](https://medium.freecodecamp.com/@BertilMuth) +> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) +> * 译者: +> * 校对者: + +# The truth is in the code # + +![](https://cdn-images-1.medium.com/max/800/1*Fw8F2fRNVfkcE-0VGyDZhQ.png) + +[shoppingapp](https://github.com/bertilmuth/requirementsascode/tree/master/requirementsascodeexamples/shoppingappjavafx) model, example of [requirementsascode](https://github.com/bertilmuth/requirementsascode) + +Sooner or later, every software developer will hear something like this: + +> “Truth can only be found in one place: the code.” + +> – Robert C. Martin, [Clean Code](https://www.amazon.de/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882) + +But what does that mean? + +The [Agile Manifesto](http://agilemanifesto.org/) values “working software over comprehensive documentation.” + +Developers write comprehensive documentation of the software’s behavior all the time, though. The code. + +Code comments, and external specifications, document the software’s behavior as well. But they may not get updated when the code changes. Then they stop reflecting the software’s behavior soon. + +In contrast, code *always* reflects the software’s behavior. It defines it. + +That’s why the truth is in the code. + +### Writing for your readers ### + +Code is documentation. Any kind of documentation should be understandable by its readers. + +The readers of code are a compiler or interpreter, and other developers. + +So it is not enough if your code compiles. Other developers need to understand it as well. They need to work on your code in the future, change it and extend it. + +A common suggestion to make code understandable is that you write clean code. Code that uses understandable language for variable and method names. That also makes a lot of code comments unnecessary. + +Clean code should express the intent: *what* somebody can achieve by calling a method. Not *how* the method achieves it. + +Guess what this method does: + + BigDecimal addUp(List ns){..} + +How about rather writing this: + + BigDecimal calculateTotal(List individualPrice){..} + +Clean code is a good idea. But I don’t think it is sufficient. + +### The importance of shared understanding ### + +When there’s a new requirement, you need to understand how implementing it affects the existing code. + +That can be a challenge if your software has been around for some time. Quite often, I have heard a dialogue like this: + +*X*: We can’t go on with feature *foo*. + +*Y*: Why? + +*X*: Cause *Z* is the only one who knows about the code. He has implemented the code that we need to change now. + +*Y:* Well, why don’t we ask him? + +*X*: Because he is sick / on vacation / at a conference / no longer at the company. + +*Y*: Oh… + +Here’s the thing. To find out if your code is understandable, somebody else should try to understand it. + +There are techniques for that. [Pair programming](https://en.m.wikipedia.org/wiki/Pair_programming) is a good one. Or you sit down with other developers. You walk them through the code you have written. + +Still, what if many developers are involved with a product? What if the development teams change their members? That makes it harder to write code that enough other people understand. + +### The story ### + +Clean code gives you the right *words*. + +The question is: what *story* will you tell with them in your code? + +I have no idea. + +But for a typical business application, I am pretty sure what story I want to read in the code. + +After introducing you to a brief example, I will outline that story. + +### The glove shop example ### + +As a user of software, I want to [reach a desired outcome](https://medium.freecodecamp.com/nobody-wants-to-use-software-a75643bee654?source=linkShare-a74297325869-1489339708). For example, I want to own a new pair of gloves to keep my fingers warm in winter. + +So I go online and see there is a new online shop specialized in gloves. The shop’s website lets me buy gloves. The “basic flow” (also called “happy day scenario”) of the use case could look like this one: + +- The system starts with an empty shopping cart. +- The system displays a list of gloves. +- I add the gloves I like to the shopping cart. The system adds the gloves to my order. +- I check out. +- I enter shipping information and payment details. The system saves this information. +- The system displays a summary of the order. +- I confirm. The system initiates shipping of my order. + +After a few days, I get my gloves. + +### Here’s the story I want to read in code. ### + +### Chapter 1: Use cases ### + +The first chapter of the story is about use cases. When I read code, I want to follow a use case in the code step by step to the desired outcome. + +I want to understand how the system reacts when something goes wrong. From a user’s perspective. + +I also want to understand the possible turns along the way. The user tries to go back from the payment details to the shipping information, for example. What happens? Is that even possible? + +I want to understand what code to look at for each part of a use case. + +#### So what are the *parts* of a use case? ### + +The fundamental part of a use case is a *step* that brings a user closer to a desired outcome. For example: “The system displays a list of gloves.” + +Not all users may be able to run a step, but only members of certain user groups (the “*actors”*). End customers buy gloves. Marketing people enter new glove offers into the system. + +The system runs some of the steps on its own. Like when it displays the gloves. No user interaction necessary there. + +Or a step is an interaction with the user. The system *reacts* to some *user event*. For example: The user enters shipping information. The system saves the information. + +I want to understand which *data* to expect with the event. Shipping information includes the user’s name, address etc. + +The user can run only a subset of steps at any given time. The user can enter payment details only after shipping information. So there is a *flow* that defines the order of the steps in a use case. And a *condition* that defines if the system can react, depending on the system’s state. + +#### To understand the code, you need an easy way to know several things. ### + +For a use case (like “buy gloves”): + +- The *flow(s)* of *steps* + +For each step: + +- Which *actors* have access to it (that is, which user groups) +- Under which *condition* the system reacts +- If the step is *autonomous*, or based on a *user interaction* +- The *system* *reaction* + +For each step that is a user interaction: + +- The *user event* (like “user entered shipping information”) +- The *data* that comes with the event + +Once I know where to find a use case and its parts in the code, I can drill deeper. + +### Chapter 2: Breaking things down into steps through components ### + +Let’s call an encapsulated, replaceable building block of your software a *component.* A component’s *responsibilities* are available to the world outside the component. + +A component could be: + +- a technical component like a database repository, +- a service like “shopping cart service”, +- an entity in your domain model. + +That depends on your software design. But no matter what your components are: you usually need several of them to realize one step of a use case. + +Let’s look at the *system reaction* of the step “The system displays a list of gloves”. You probably need to develop at least two *responsibilities*. One finds the gloves in the database, and one turns the list of gloves into a webpage. + +When reading code I want to understand the following things: + +- What are a component’s *responsibilities.* For example: “find gloves” for the database repository. +- What are the *inputs* / *outputs* of each responsibility. Example input: criteria for which gloves to find. Example output: list of gloves. +- Who *coordinates* the responsibilities. For example: find gloves first. Turn result into a webpage second. + +### Chapter 3: What components do ### + +A component’s code fulfills responsibilities. + +That often happens in a *domain model*. The domain model uses terms relevant in the business domain. + +For the example, a term could be Glove. Another term could be Order. + +The domain model describes the *data* for each term. Each Glove has a color, a brand, a size, a price and so on. + +The domain model also describes computations on the data. The total price of an Order is the sum of the prices of each Glove bought by the user. + +A component can also be a technical component like a database repository. The code needs to answer: How does the repository create, find, update and delete elements in the database? + +### Telling your story ### + +Maybe your story looks similar to the one above. Maybe it’s different. Whatever your story is, programming languages give you great freedom to express yourself and tell that story. + +That’s a good thing because it allows developers to adapt to different contexts and requirements. + +It also bears the risk that developers tell too many different stories. Even for the same product. That makes it harder than necessary to understand code that somebody else has written. + +One way to address this is the use of design patterns. They allow you to structure your code. You can agree on that common structure in your team or even across teams. + +For example, the Rails framework is based on the well-known Model View Controller pattern. + +The model is the place for *domaindata.* + +The view is the client side user interface, like HTML pages. It is the origin of *userevents.* + +The controller receives the user events on server side. It is responsible for *flow.* + +So if several developers use Rails, they know which part of the code to look at for certain parts of their story. + +They could find out what is missing when sharing their understanding. Then, they could agree on further conventions on where to put which part of their story. + +If that works for you, that is just fine. But I want to go further than that. + +### Requirements as Code ### + +Many of my clients ask me how to deal with long term software documentation. + +When working in an agile context, how do you create documentation for software maintenance? + +What requirements have been implemented so far? + +Where do you find their realization in the code? + +For a long time I had no satisfying answer. Except, of course: the importance of well written, automated tests. Clean production code. Shared understanding. + +But a few years ago, I started thinking: + +> If the truth is in the code, the code should be able to speak the truth. + +In other words: if you took great care of telling your story in the code, why would you want to tell it again? + +There needs to be a better way. It must be possible to extract the story, and generate documentation from it. Documentation that non-technical stakeholders understand as well. + +Documentation that is always up-to-date, because it comes from the same source that defines the software’s behavior. + +The only reliable source: the code itself. + +After a lot of experiments, I had some results. I made them public in a Github project called [requirementsascode](https://github.com/bertilmuth/requirementsascode). + +### How it works ### + +![](https://cdn-images-1.medium.com/max/800/1*rZAA0h24T9SdEZYdE7stIQ@2x.png) + +- A UseCaseModel instance defines the *actors*, *use cases*, their *flows* and *steps*. It tells chapter 1 of the story. You find an example of such a model at the start of this article. +- A use case model configures UseCaseModelRunner instances. Every user has her own runner, because every user may take a different path through the use cases in the model. +- The runner reacts to a *user event* from the frontend by calling the *system reaction* in the backend.The frontend communicates to the backend only through the runner. +- But the runner only reacts if the user is at the right position of the *flow* and the step’s *condition* is fulfilled. For example, the runner only reacts to the “EnterPaymentDetails“ event if the user has entered the shipping information right before. +- The *systemreaction* is a single method. The method’s body is responsible for coordinating the components to realize the step, as described in chapter 2. +- Chapter 3 is out of scope of requirementsascode. It is left up to the application. That makes requirementsascode compatible with arbitrary software designs. + +So the UseCaseModelRunner controls the user visible behavior of the software. Based on a UseCaseModel. + +With [requirementsascodeextract](https://github.com/bertilmuth/requirementsascode/tree/master/requirementsascodeextract), you can generate documentation from the same use case model that configures the runner. That way, the documentation always reflects how the software works. + +Requirementsascodeextract uses the FreeMarker template engine. That allows you to generate any plain text documentation you like. For example HTML pages. Further processing could turn it into other documentation formats, like PDF. + +### Your feedback will help me improve this project ### + +I started working on requirementsascode several years ago, but just recently made the project public. It has gone through significant improvement since the beginning. + +To learn whether the approach scales, I tried it on an application with several thousand lines of code. It worked. I tried it on smaller applications as well. + +Still, so far, requirementsascode has been my hobby project. + +That’s why I need your help. Please give me feedback. + +What do you think of the idea? Can you imagine that it works in the context of the software you develop? Any other feedback? + +You can drop me a note in the comments or contact me on [Twitter](https://twitter.com/BertilMuth) or [LinkedIn](https://www.linkedin.com/in/bertilmuth). + +You can [clone](https://github.com/bertilmuth/requirementsascode) the project and try it out yourself. + +Or you can [contribute](https://github.com/bertilmuth/requirementsascode/blob/master/CONTRIBUTING.md) to documenting the truth in the code. + +--- + +> [掘金翻译计划](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)、[React](https://github.com/xitu/gold-miner#react)、[前端](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)。 From 6a1db73aa1c1bc6117e630f7ecfc746a9f100a7c Mon Sep 17 00:00:00 2001 From: rottenpen <522876037@qq.com> Date: Wed, 26 Apr 2017 22:31:24 +0800 Subject: [PATCH 270/638] =?UTF-8?q?=E6=A0=A1=E5=AF=B91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO/function-as-child-components.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO/function-as-child-components.md b/TODO/function-as-child-components.md index a1879636005..1843a42e6e6 100644 --- a/TODO/function-as-child-components.md +++ b/TODO/function-as-child-components.md @@ -2,7 +2,7 @@ > * 原文作者:[Merrick](http://merrickchristensen.com/about.html) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 译者:[rottenpen](https://github.com/rottenpen) -> * 校对者: +> * 校对者:[loveky](https://github.com/loveky) [avocadowang](https://github.com/avocadowang) # 函数式子类组件 #(这个名字待定) From 70611c0381575e0d9ca80b2ccc2b0874cb614529 Mon Sep 17 00:00:00 2001 From: rottenpen <522876037@qq.com> Date: Wed, 26 Apr 2017 22:32:40 +0800 Subject: [PATCH 271/638] =?UTF-8?q?=E6=A0=A1=E5=AF=B91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO/function-as-child-components.md | 41 ++++++++++++++-------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/TODO/function-as-child-components.md b/TODO/function-as-child-components.md index 1843a42e6e6..08dae3363a3 100644 --- a/TODO/function-as-child-components.md +++ b/TODO/function-as-child-components.md @@ -5,21 +5,21 @@ > * 校对者:[loveky](https://github.com/loveky) [avocadowang](https://github.com/avocadowang) -# 函数式子类组件 #(这个名字待定) +# 将函数作为子类的组件 # -我最近在 Twitter 上关注了高阶组件和函数式子类组件,得到的结果让我很惊喜。 +我最近在 Twitter 上发起了关于高阶组件和将函数作为子类的组件的投票,得到的结果让我很惊喜。 -如果你不知什么是“函数式子类组件”,我试图通过这篇文章告诉你: +如果你不知什么是“函数作为子类的组件”,我试图通过这篇文章告诉你: -1. 是什么。 +1. 函数作为子类的组件是什么。 -2. 为什么有用。 +2. 它为什么有用。 -3. 我只想享受分享的快乐,而获取不是点赞,转发或者别的,你懂我的意思吧? +3. 我只想享受分享的快乐,而不是收获一些Twitter转发,点赞,或是上一些newsletter等等。你懂我的意思吧? -## 什么是函数式子组件? ## +## 什么是函数作为子类的组件? ## -“函数式子类组件”是接收一个函数当作父组件的子类组件。 +“函数作为子类的组件”是接收一个函数当作父组件的子类组件。 ``` classMyComponentextendsReact.Component{ @@ -38,7 +38,7 @@ MyComponent.propTypes = { ``` -没错!通过子类组件函数我们就能解耦父类组件和他们的子类,让设计者决定选用哪些参数及怎么将参数应用于子类组件。例如: +没错!通过子类函数组件我们就能解耦父类组件和它们的子类,让设计者决定选用哪些参数及怎么将参数应用于子类组件。例如: ``` @@ -59,11 +59,11 @@ MyComponent.propTypes = { ``` -对 MyComponent 最贴切的说法是,函数子组件作为父类管理状态的组件,而不是对其状态的利用。让我们再来一个更真实的例子。 +对 MyComponent 最贴切的说法是,子类组件函数作为父类管理状态的组件,而不是对其状态的利用。让我们再来一个更真实的例子。 ### 百分比组件 ### -Ratio 组件将使用设备的宽度,监听 resize 事件和返回它的子类们一个宽度,高度和一些关于是否计算了尺寸的信息。 +Ratio 组件将使用设备的宽度,监听 resize 事件并将宽度、高度以及一些描述是否完成尺寸计算的信息传给它的子组件。 首先我们从函数子组件的片段开始,这片段在所有函数子组件中都是常见的,它只是让消费者知道我们期望一个函数作为子组件,而不是 React 节点。 @@ -188,13 +188,13 @@ Ratio.defaultProps = { ``` -好吧,在这我做了很多东西。我们添加了一些事件监听来监听 resize 事件以及使用提供的比率计算实际的宽度高度。所以我们得到了一个宽高在组件的 state 里,那我们如何与其他组件分享它们呢? +好吧,在这我做了很多东西。我们添加了一些事件监听来监听 resize 事件以及使用提供的比率计算实际的宽度高度。所以我们得到的宽高在组件的 state 里,那我们如何与其他组件分享它们呢? 这是一件难以理解的事情,因为它很容易让人认为“这不可能是全部”,但事实这就是全部了。 #### 子类组件只是一个 javascript 函数 #### -这意味着为了计算宽度和高度,我们只需要提供参数: +这意味着想要计算出宽度和高度,我们只需要提供参数: ``` render() { @@ -207,7 +207,7 @@ render() { ``` -现在任何人都可以使用比例组件通过提供的宽度以他们喜欢的方式来正确计算出高度!例如,有人可以使用比例组件来设置img上的比例: +现在任何人都可以使用比例组件通过提供的宽度以他们喜欢的方式来正确计算出高度!例如,有人可以使用比例组件来设置 img 上的比例: ``` @@ -244,7 +244,7 @@ render() { ### 优势 ### 1. 构造组件的开发人员能自主控制如何传递和使用这些属性。 -2. 函数式子组件的作者不强制组件的值如何被利用,允许它非常灵活的使用。 +2. 函数作为子类的组件的作者不强制组件的值如何被利用,允许它非常灵活的使用。 3. Comsumer 不需要创建另一个组件来决定怎样从“高阶组件”传入属性。高阶组件通常在组成的组件上强制执行属性名称。 为了解决这个问题,许多“高阶组件”提供了一个选择器函数,允许消费者选择你的属性名称(请参考 redux 连接选择功能)。这不是函数子组件的问题。 4. 不污染 “props” 命名空间,这允许你同时使用 “Ratio” 组件和 “Pinch to Zoom” 组件,不管它们是否都会计算宽度。高阶组件带有与它们组成的组件相关的隐式契约,不幸的是这可能意味着 prop 的名称会发生冲突以至于高阶组件无法与其他组件进行组合。 5. 高阶组件在你的开发工具和组件本身中创建一个间接层,例如设置在组件上的常量被高阶组件封装后将无法使用。例如: @@ -261,29 +261,28 @@ exportdefault connect(...., MyComponent); ``` . -RIP 你的常数(?)。如果没有高阶组件提供的函数则不可再访问底层组件。哭。 +让你的常数安息吧。因为如果没有高阶组件提供的函数,你将再也不能访问到这个常量。哭。 #### 总结 #### -大多数时候我们会认为“我需要一个高阶组件来实现这个共享功能!”根据我的经验,我相信在多数情况下函数式子组件是一个更好的替代方法来抽象你的UI问题,除非你的子组件与其组合的高阶组件真正耦合。 +大多数时候我们会认为“我需要一个高阶组件来实现这个共享功能!”根据我的经验,我相信在多数情况下函数作为子类的组件是一个更好的替代方法来抽象你的 UI 问题,除非你的子组件与其组合的高阶组件真正耦合。 #### 关于高阶组件的不幸事实 #### 补充一下,我认为高阶组件的名称不正确,尽管现在尝试修改已经有点晚了。高阶函数是至少执行一下操作之一的函数: 1. 将n个函数作为参数。 2. 返回一个函数作为结果。 -Indeed Higher Order Components do something similar to this, namely take a Component as and argument and return a Component but I think it is easier to think of a Higher Order Component as a factory function, it is a function that dynamically creates a component to allow for runtime composition of your components. However, they are **unaware** of your React state and props at composition time! 事实上,高阶组件做了类似的事情,也就是拿一个组件作为参数并返回一个组件,但是我更容易将高阶组件看作是工厂函数,它是一个能动态创建的组件将允许的功能用于组件的运行组合。然而,在运行组合的时候他们是**不知道**你的 React 的 state 和 props 。 -函数式子组件允许你的组件们在作出组合决策时可以访问 state,props 和上下文。当函数作为子组件: +函数作为子类的组件允许你的组件们在作出组合决策时可以访问 state , props 和上下文。当函数作为子组件: 1. 将一个函数作为参数。 2. 渲染此函数的结果。 -我不禁觉得它们应该得到“高阶组件”的标题,因为它像高阶函数只使用组件组合技术而不是功能组合。好吧,现在我们还是继续用“函数式子组件”这个粗暴的名字。 +我不禁觉得它们应该得到“高阶组件”的标题,因为它像高阶函数只使用组件组合技术而不是功能组合。好吧,现在我们还是继续用“将函数作为子类的组件”这个粗暴的名字。 ### 例子 ### 1. [Pinch to Zoom - Function as Child Component](https://gist.github.com/iammerrick/c4bbac856222d65d3a11dad1c42bdcca) -2. [react-motion](https://github.com/chenglou/react-motion) 这个项目在讲过很长一段时间的高阶组件转换后才引进了这个概念。 +2. [react-motion](https://github.com/chenglou/react-motion) 这个项目在讲了很长一段时间这个概念之后,高阶组件才演变出函数子组件。 --- > [掘金翻译计划](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)、[React](https://github.com/xitu/gold-miner#react)、[前端](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)。 From 6f830b3a83829b613cbdd7e044f5c0b2e9cf4e74 Mon Sep 17 00:00:00 2001 From: rottenpen <522876037@qq.com> Date: Wed, 26 Apr 2017 22:33:06 +0800 Subject: [PATCH 272/638] =?UTF-8?q?=E6=A0=A1=E5=AF=B91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO/function-as-child-components.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO/function-as-child-components.md b/TODO/function-as-child-components.md index 08dae3363a3..10f18cb7a22 100644 --- a/TODO/function-as-child-components.md +++ b/TODO/function-as-child-components.md @@ -65,7 +65,7 @@ MyComponent.propTypes = { Ratio 组件将使用设备的宽度,监听 resize 事件并将宽度、高度以及一些描述是否完成尺寸计算的信息传给它的子组件。 -首先我们从函数子组件的片段开始,这片段在所有函数子组件中都是常见的,它只是让消费者知道我们期望一个函数作为子组件,而不是 React 节点。 +首先我们从函数作为子类的组件的片段开始,这片段在所有子组件函数中都是常见的,它只是让消费者知道我们期望一个函数作为子组件,而不是 React 节点。 ``` classRatioextendsReact.Component{ From 30cb0cdf0d440f27019f87db7cf4a1a098c47e62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=B9=E5=8F=B7=E4=B8=89?= Date: Wed, 26 Apr 2017 22:51:02 +0800 Subject: [PATCH 273/638] Create a-case-for-using-storyboards-on-ios.md --- TODO/a-case-for-using-storyboards-on-ios.md | 147 ++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 TODO/a-case-for-using-storyboards-on-ios.md diff --git a/TODO/a-case-for-using-storyboards-on-ios.md b/TODO/a-case-for-using-storyboards-on-ios.md new file mode 100644 index 00000000000..9c29d17904d --- /dev/null +++ b/TODO/a-case-for-using-storyboards-on-ios.md @@ -0,0 +1,147 @@ +> * 原文地址:[A Case For Using Storyboards on iOS](https://medium.cobeisfresh.com/a-case-for-using-storyboards-on-ios-3bbe69efbdf4) +> * 原文作者:[Marin Benčević](https://medium.cobeisfresh.com/@marinbenc) +> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) +> * 译者: +> * 校对者: + +# A Case For Using Storyboards on iOS # + +![](https://cdn-images-1.medium.com/max/2000/1*YsN0CVtTY3I5d6UUEtUv6Q.png) + +I’ve seen a lot of articles recently that argue against using storyboards when creating iOS apps. The most commonly mentioned arguments are that storyboards are not human readable, they are slow and they cause git conflicts. These are all valid concerns, but can be avoided. I want to tell you how we use storyboards on non-trivial projects, and how you can avoid these concerns and still get the nice things storyboards give you. + +#### Why use storyboards? + +> A picture is worth a thousand words. + +Humans are visual thinkers. The vast majority of information we receive is through our eyes, and our brains are incredibly complex visual pattern matching machines, which help us understand the world around us. + +Storyboards give you an overview of a screen in your app, unmatched by code representation, whether it’s XML or plain Swift. When you open up a storyboard, you can see all views, their positions and their hierarchies in a second. For each view, you can see all the constraints that affect it, and how it interacts with other views. The efficiency of transferring information visually can’t be matched with text. + +Another benefit of storyboards is that it makes auto layout more intuitive. Auto layout is an inherently visual system. It might be a set of mathematical equations under the hood, but we don’t think like that. We think in terms of “this view needs to be next to this one at all times”. Doing auto layout visually is a natural fit. + +![](https://cdn-images-1.medium.com/max/800/1*MS3ALafvQX2fmK-5onF0SQ.png) + +Also, doing auto layout in storyboards gives you some compile-time safety. Most missing or ambiguous constraints are caught during the creation of the layout, not when you open the app. This means less time spent on tracking down ambiguous layouts, or finding out why a view is missing from the screen. + +#### How you should do it #### + +**One storyboard per UIViewController** + +![](https://cdn-images-1.medium.com/max/800/1*5MgjKAMD4kH-3clAiaDT2A.png) + +You wouldn’t write your whole app inside a single UIViewController. The same goes for storyboards. Each view controller deserves its own storyboard. This has several advantages. + +1. **Git conflicts occur only if two developers are working on the same UIViewController in a storyboard at the same time.** In my experience, this doesn’t happen often, and it’s not hard to fix when it does. + +2. **The storyboard is no longer slow to load, since it only loads one UIViewController.** + +3. **You are free to instantiate any UIViewController whichever way you like, just by getting the initial view controller of a storyboard.** Whether you’re using segues or pushing them through code. + +When I’m creating a new screen, my first step is to create a UIViewController. Once I did that, I create a storyboard **with the same name** as the view controller I just created. This lets you do a pretty cool thing: instantiate UIViewControllers in a type safe way, without hard-coded strings. + + let feed = FeedViewController.instance() + // `feed` is of type `FeedViewController` + +This method works by finding a storyboard with the same name as the class name, and getting the initial view controller from that storyboard. + +I know that’s how NIBs are used. But the NIB format is outdated, and some features (like creating UITableViewCells in the actual UIViewController’s nib) are not supported in the .xib editor. I have a feeling that the list of unsupported features will only grow, and that’s why I use storyboards over nibs. + +**No segues** + +Segues seem cool at first, but as soon as you have to transmit data from one screen to the next, it becomes a pain. You have to store the data in some temporary variable somewhere, and then set that value inside the `prepare(for segue:, sender:)` method. + + class UsersViewController: UIViewController, UITableViewDelegate { + + private enum SegueIdentifier { + static let showUserDetails = "showUserDetails" + } + + var usernames: [String] = ["Marin"] + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + usernameToSend = usernames[indexPath.row] + performSegue(withIdentifier: SegueIdentifier.showUserDetails, sender: nil) + } + + + private var usernameToSend: String? + + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + + switch segue.identifier { + case SegueIdentifier.showUserDetails?: + + guard let usernameToSend = usernameToSend else { + assertionFailure("No username provided!") + return + } + + let destination = segue.destination as! UserDetailViewController + destination.username = usernameToSend + + default: + break + } + } + + } + +This code has a lot of problems. `prepare(for:sender:)` is not a pure function since it depends on the temporary variable defined above it. Even worse, that variable is optional, and it’s unclear what should happen if it’s nil. + +You need to remember to manually set the *usernameToSend* property, which adds mutable state to our code. You also need to cast the segue’s destination to the type you expect. That’s lot of boilerplate and more than one point of failure. + +I would much rather have a function that takes a non-optional value, and pushes the next view controller with that value. Simple and easy. + + class UsersViewController: UIViewController, UITableViewDelegate { + + var usernames: [String] = ["Marin"] + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let username = usernames[indexPath.row] + showDetail(withUsername: username) + } + + private func showDetail(withUsername username: String) { + let detail = UserDetailViewController.instance() + detail.username = username + navigationController?.pushViewController(detail, animated: true) + } + + } + +This code is much safer, more readable and more concise. + +**All properties are set in code** + +Leave all storyboard values at their defaults. If a label needs to have a different text, or a view needs to have a background color, those things are done in code. This relates especially to all the little checkmarks in the property inspector. + +![](https://cdn-images-1.medium.com/max/800/1*QQ6_kcvyx1Z1vHdUYsc77A.png) + +The reason is that you don’t want to hard-code fonts, colors and texts. You can have constants for those, and a single place where they are kept, so you have a single place to change when you need to make a design change. + +Also, scanning the code for view properties is easier than trying to find which checkmarks are checked in the storyboard. + +This means you can build auto layout and views in the storyboard, but [style them in code](https://medium.cobeisfresh.com/composable-type-safe-uiview-styling-with-swift-functions-8be417da947f), which gives you complete freedom to create reusable code and a human-readable change history. + +#### What storyboards are for me #### + +You might be reading this article and thinking “This guy says storyboards are great, and then says he doesn’t use half of the features!”, and you’re right! + +Storyboards do have problems, and these are the ways I avoid those problems. I find storyboards very useful for what I want to do with them: create the view hierarchy and constraints. Nothing more, nothing less. + +My point is to not disregard a whole technology because you don’t like one aspect of it. You are free to pick and choose which parts you want to use. **It’s not all or nothing.** + +So for those of you who want the benefits or storyboards, but want to minimize the downsides, this is our approach that has worked very well so far. If you have any comments, feel free to leave a response or hit me up on @marinbenc on Twitter. + +*If you liked this one, check out some some other articles from my team:* + +[![Markdown](http://i4.buimg.com/1949/a68c5bee9aecbe65.png)](https://medium.cobeisfresh.com/how-to-win-a-hackathon-tips-tricks-8cd391e18705) + +[![Markdown](http://i4.buimg.com/1949/d5fb464e8b33d195.png)](https://medium.cobeisfresh.com/accessing-types-from-extensions-in-swift-32ca87ec5190) + + + +--- + +> [掘金翻译计划](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)、[React](https://github.com/xitu/gold-miner#react)、[前端](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)。 From d65c6cc1fb9b6a75beb0f558bb076262666830dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=B9=E5=8F=B7=E4=B8=89?= Date: Wed, 26 Apr 2017 23:16:25 +0800 Subject: [PATCH 274/638] Create css-in-javascript-the-future-of-component-based-styling.md --- ...t-the-future-of-component-based-styling.md | 369 ++++++++++++++++++ 1 file changed, 369 insertions(+) create mode 100644 TODO/css-in-javascript-the-future-of-component-based-styling.md diff --git a/TODO/css-in-javascript-the-future-of-component-based-styling.md b/TODO/css-in-javascript-the-future-of-component-based-styling.md new file mode 100644 index 00000000000..9da5e81bf28 --- /dev/null +++ b/TODO/css-in-javascript-the-future-of-component-based-styling.md @@ -0,0 +1,369 @@ +> * 原文地址:[CSS in JavaScript: The future of component-based styling](https://medium.freecodecamp.com/css-in-javascript-the-future-of-component-based-styling-70b161a79a32) +> * 原文作者:[Jonathan Z. White](https://medium.freecodecamp.com/@JonathanZWhite) +> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) +> * 译者: +> * 校对者: + +# CSS in JavaScript: The future of component-based styling # + +![](https://cdn-images-1.medium.com/max/1000/1*yVKDbwtvfoakj3RZ9g8ARQ.png) + +Image by [@jonathanzwhite](https://twitter.com/JonathanZWhite) + +By adopting inline styles, we can get all of the programmatic affordances of JavaScript.This gives us the benefits of something like a CSS pre-processor (variables, mixins, and functions). It also solves a lot of the problems that CSS has, such as global namespacing and styling conflicts. + +For a deep dive into the problems that CSS in JavaScript solves, check out the famous presentation: [React CSS in JS](https://speakerdeck.com/vjeux/react-css-in-js). For a case study on the performance improvements you get from Aphrodite, you can read [Inline CSS at Khan Academy: Aphrodite](http://engineering.khanacademy.org/posts/aphrodite-inline-css.htm). If you want to learn more about CSS in JavaScript best practices, check out [Airbnb’s styleguide](https://github.com/airbnb/javascript/tree/master/css-in-javascript). + +In addition we’ll be using inline JavaScript styles to build components to address some of the fundamentals of design I covered in one of my previous articles: [Before you can master design, you must first master the fundamentals](https://medium.freecodecamp.com/before-you-can-master-design-you-must-first-master-the-fundamentals-1981a2af1fda). + +### A motivating example ### + +Let’s start off with a simple example: creating and styling a button. + +Normally the component and its associated styles would go in the same file: `Button` and `ButtonStyles`. This is because they fall under the same concern: the view. However, for this example, I broke up the code into multiple gists to make it more digestible. + +Here’s the button component: + +``` +... + +function Button(props) { + return ( + + ); +} +``` + +This is nothing unexpected — just a stateless React component. Where Aphrodite comes into play is in the `className` property. The function `css` takes in a `styles` object and converts it into css. The `styles` object is created with Aphrodite’s `StyleSheet.create({ ... })` function. You can see the output of `StyleSheet.create({ ... })` with this [Aphrodite playground](https://output.jsbin.com/qoseye?). + +**Here is the button stylesheet:** + +``` +... + +const gradient = 'linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)'; + +const styles = StyleSheet.create({ + button: { + background: gradient, + borderRadius: '3px', + border: 0, + color: 'white', + height: '48px', + textTransform: 'uppercase', + padding: '0 25px', + boxShadow: '0 3px 5px 2px rgba(255, 105, 135, .30)', + }, +}); +``` + +One of the benefits of Aphrodite is that migration is straightforward and the learning curve is low. Properties like `border-radius` become `borderRadius` and values become strings. Pseudo-selectors, media queries, and font definitions all work. In addition, vendor prefixes are added automatically. + +**Here is the result:** + +![](https://cdn-images-1.medium.com/max/800/1*x1ccRv9UGvcxBvz4TvC4Qg.png) + +[One of the benefits of Aphrodite is that migration is straightforward and the learning curve is low.](https://twitter.com/JonathanZWhite) + +With this example in mind, **let’s take a look at how we can use Aphrodite to build a basic visual design system**, focusing on the following design fundamentals: typography and spacing. + + +### Fundamental №1 —Typography ### + +Let’s start off with typography, a fundamental basis for design. **The first step is to define typography constants**. And unlike Sass or Less, constants for Aphrodite can go in a JavaScript or JSON file. + +#### Define typography constants + +When creating constants, **use semantic names for your variables**. For example, instead of naming one of your font-sizes `h2`, use a name like `displayLarge` that describes its *role*. Similarly, for font-weights, instead of naming one of your weights `600`, give it a name like `semibold` to describe its *effect*. + +``` +export const fontSize = { + // heading + displayLarge: '32px', + displayMedium: '26px', + displaySmall: '20px', + heading: '18px', + subheading: '16px', + + // body + body: '17px', + caption: '15px', +}; + +export const fontWeight = { + bold: 700, + semibold: 600, + normal: 400, + light: 200, +}; + +export const tagMapping = { + h1: 'displayLarge', + h2: 'displayMedium', + h3: 'displaySmall', + h4: 'heading', + h5: 'subheading', +}; + +export const lineHeight = { + // heading + displayLarge: '48px', + displayMedium: '48px', + displaySmall: '24px', + heading: '24px', + subheading: '24px', + + // body + body: '24px', + caption: '24px', +}; +``` + +It’s important to get the values right for variables like font-sizes and line-heights. This is because they directly affect the vertical rhythm within a design. Vertical rhythm is a concept that helps you achieve consistent spacing between elements. + +For more on vertical rhythm, you can read this article: [Why is Vertical Rhythm an Important Typography Practice?](https://zellwk.com/blog/why-vertical-rhythms/) + +![](https://cdn-images-1.medium.com/max/800/1*Ehj9XMvQ9wJNhxWNqwXfKw.png) + +[Use a calculator to determine line-heights](https://drewish.com/tools/vertical-rhythm/) + +There is a science behind choosing the values for your line-heights and font-sizes. We can use mathematic ratios to generate a set of potential sizes candidates. A few weeks ago, I wrote an article detailing the methodology: [Typography can make or break your design: a process for choosing type](https://medium.freecodecamp.com/typography-can-make-your-design-or-break-it-7be710aadcfe). For determining font-sizes, you use [Modular Scale](http://www.modularscale.com/). For determining line-heights, you can use this [vertical rhythm calculator](https://drewish.com/tools/vertical-rhythm/). + +#### Define a heading component #### + +After defining our typography constants, the next step is to create a component to consume the values. **The goal of the component is to enforce consistency in design and implementation for headings across the codebase.** + +``` +import React, { PropTypes } from 'react'; +import { StyleSheet, css } from 'aphrodite/no-important'; +import { tagMapping, fontSize, fontWeight, lineHeight } from '../styles/base/typography'; + +function Heading(props) { + const { children, tag: Tag } = props; + return {children}; +} + +export default Heading; + +export const styles = StyleSheet.create({ + displayLarge: { + fontSize: fontSize.displayLarge, + fontWeight: fontWeight.bold, + lineHeight: lineHeight.displayLarge, + }, + displayMedium: { + fontSize: fontSize.displayMedium, + fontWeight: fontWeight.normal, + lineHeight: lineHeight.displayLarge, + }, + displaySmall: { + fontSize: fontSize.displaySmall, + fontWeight: fontWeight.bold, + lineHeight: lineHeight.displaySmall, + }, + heading: { + fontSize: fontSize.heading, + fontWeight: fontWeight.bold, + lineHeight: lineHeight.heading, + }, + subheading: { + fontSize: fontSize.subheading, + fontWeight: fontWeight.bold, + lineHeight: lineHeight.subheading, + }, + }); +``` + +The `Heading` component is a stateless function that takes in a tag as a property and returns the tag with its associated style. This is possible because we defined the tag mappings earlier in the constants file. + +``` +... +export const tagMapping = { + h1: 'displayLarge', + h2: 'displayMedium', + h3: 'displaySmall', + h4: 'heading', + h5: 'subheading', +}; +``` + +At the bottom of the component file, we define our `styles` object. This is where we use the typography constants. + +``` +export const styles = StyleSheet.create({ + displayLarge: { + fontSize: fontSize.displayLarge, + fontWeight: fontWeight.bold, + lineHeight: lineHeight.displayLarge, + }, + + ... +}); +``` + +And this is how the `Heading` component would be used: + +``` +function Parent() { + return ( + Hello World + ); +} +``` + +With this approach, **we reduce unexpected variability in our type system**. We avoid the pitfall of a hundred different font sizes by removing the need for global styles and standardizing headings across the codebase. In addition, this approach we took to building the `Heading` component can be applied to building a `Text` component for body copy. + +### Fundamental №2 — Spacing ### + +**Spacing controls both vertical and horizontal rhythm in design**. That makes spacing pivotal to establishing a visual design system. Just like in the typography section, the first step to address spacing is to define spacing constants. + +#### Define spacing constants ### + +When defining spacing constants for the margins between elements, we can adopt a mathematic approach. Using a `spacingFactor` constant, we can generate a set of distances based on a common factor. **This approach ensures that we have logical and consistent spacing between elements.** + +``` +const spacingFactor = 8; +export const spacing = { + space0: `${spacingFactor / 2}px`, // 4 + space1: `${spacingFactor}px`, // 8 + space2: `${spacingFactor * 2}px`, // 16 + space3: `${spacingFactor * 3}px`, // 24 + space4: `${spacingFactor * 4}px`, // 32 + space5: `${spacingFactor * 5}px`, // 40 + space6: `${spacingFactor * 6}px`, // 48 + + space8: `${spacingFactor * 8}px`, // 64 + space9: `${spacingFactor * 9}px`, // 72 + space13: `${spacingFactor * 13}px`, // 104 +}; +``` + +The example above uses a linear scale, one to thirteen. However, experiment with different scales and ratios. Designs require different scales based on their purpose, their audience, and the devices they target. As an example,** here are the first six computed distances using the golden ratio** with a `spacingFactor` of eight. + + Golden Ratio (1:1.618) + + 8.0 x (1.618 ^ 0) = 8.000 + 8.0 x (1.618 ^ 1) = 12.94 + 8.0 x (1.618 ^ 2) = 20.94 + 8.0 x (1.618 ^ 3) = 33.89 + 8.0 x (1.618 ^ 4) = 54.82 + 8.0 x (1.618 ^ 5) = 88.71 + +This is what the spacing scale would look like in code. I added a helper function to handle the computation and round off the output to its nearest pixel value. + +``` +const spacingFactor = 8; +export const spacing = { + space0: `${computeGoldenRatio(spacingFactor, 0)}px`, // 8 + space1: `${computeGoldenRatio(spacingFactor, 1)}px`, // 13 + space2: `${computeGoldenRatio(spacingFactor, 2)}px`, // 21 + space3: `${computeGoldenRatio(spacingFactor, 3)}px`, // 34 + space4: `${computeGoldenRatio(spacingFactor, 4)}px`, // 55 + space5: `${computeGoldenRatio(spacingFactor, 5)}px`, // 89 +}; + +function computeGoldenRatio(spacingFactor, exp) { + return Math.round(spacingFactor * Math.pow(1.618, exp)); +} +``` + +After defining our spacing constants, we can use them to add margins to elements in our design. **One approach we can take is to import the spacing constants and consume them in components**. + +For example, let’s add a `marginBottom` to the `Button` component. + +``` +import { spacing } from '../styles/base/spacing'; + +... + +const styles = StyleSheet.create({ + button: { + marginBottom: spacing.space4, // adding margin using spacing constant + ... + }, +}); +``` + +This works in most scenarios. However, what happens if we want to change the `marginBottom` property of the button based on where the button is place? + +One way to achieve variable margins is to override the margin style from the consuming parent component. An alternative approach is to **create a **`Spacing`**component to control the vertical margins on elements**. + +``` +import React, { PropTypes } from 'react'; +import { spacing } from '../../base/spacing'; + +function getSpacingSize(size) { + return `space${size}`; +} + +function Spacing(props) { + return ( +
+ {props.children} +
+ ); +} + +export default Spacing; +``` + +Using this approach, we can remove the responsibility of setting margins out of the child component and into the parent component. **This way, the child component becomes layout agnostic, not requiring any knowledge of where to place itself in relation to other elements.** + +This works because components like buttons, inputs, and cards may need variable margins. For example, a button in a form may need larger margins than a button in a navigation bar. The caveat is that if a component always has consistent margins, then it would make more sense to handle margins within the component. + +Also you may have noticed that the examples only use `marginBottom`. This is because **defining all your vertical margins in one direction allows you avoid collapsing margins and keep track of the vertical rhythm of your design**. You can read more on this in Harry Robert’s article, [Single-direction margin declarations](https://csswizardry.com/2012/06/single-direction-margin-declarations/). + +On a final note, you can also use the spacing constants you defined as padding. + +``` +import React, { PropTypes } from 'react'; +import { StyleSheet, css } from 'aphrodite/no-important'; +import { spacing } from '../../styles/base/spacing'; + +function Card(props) { + return ( +
+ {props.children} +
+ ); +} + +export default Card; + +export const styles = StyleSheet.create({ + card: { + padding: spacing.space4}, // using spacing constants as padding + + background: 'rgba(255, 255, 255, 1.0)', + boxShadow: '0 3px 17px 2px rgba(0, 0, 0, .05)', + borderRadius: '3px', + }, +}); +``` + +By using using the same spacing constants for both margins and padding, you can achieve more visual consistency in your design. + +Here’s what the result might look like: + +![](https://cdn-images-1.medium.com/max/800/1*oDkbVmgCJ4ss5fuRNvzoUg.png) + +[By using spacing constants for your margins and padding, you can achieve more visual consistency.](https://twitter.com/JonathanZWhite) + +Now that you have a grasp of CSS in JavaScript, go out and experiment. Try incorporating inline JavaScript styles into your next project. I think **you’ll appreciate being able to work in a single context to handle all of your styling and view concerns**. + +On the topic of CSS and JavaScript, what are some new developments that you’re excited about? Personally I’m excited about async/await. Leave me a note or send me a [tweet](https://twitter.com/jonathanzwhite) on Twitter. + +You can find me on Medium where I publish every week. Or you can follow me on [Twitter](https://twitter.com/JonathanZWhite), where I post non-sensical ramblings about design, front-end development, and virtual reality. + +*P.S. If you enjoyed this article, it would mean a lot if you clicked the 💚 and shared with friends.* + +[![](https://cdn-images-1.medium.com/max/600/1*mxQhZLqG7l5dMLvxYAklgw.png)](http://mrwhite.space/signup) + +[![](https://cdn-images-1.medium.com/max/600/1*UOsjAdUZ9O0QSyfXOpQPbA.png)](https://twitter.com/JonathanZWhite) +--- + +> [掘金翻译计划](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)、[React](https://github.com/xitu/gold-miner#react)、[前端](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)。 From e378240abe0d8e1c7be8382330eca708f72993e8 Mon Sep 17 00:00:00 2001 From: lsvih Date: Thu, 27 Apr 2017 08:34:14 +0800 Subject: [PATCH 275/638] Update debugging-tips-tricks.md --- TODO/debugging-tips-tricks.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/TODO/debugging-tips-tricks.md b/TODO/debugging-tips-tricks.md index ecbda059dc2..0c8afa5cdbe 100644 --- a/TODO/debugging-tips-tricks.md +++ b/TODO/debugging-tips-tricks.md @@ -12,12 +12,12 @@ #### 隔离问题 #### -隔离问题大概是 debug 中最重要的核心概念。我们的代码库是由不同的类库、框架组成的,它们有着许多的贡献者,甚至还有一些不再参与项目的人也为其提供过代码,因此我们的代码库是杂乱无章的。隔离问题可以帮助我们逐步剥离与问题无关的部分以便我们可以把注意力放在解决方案上。 +隔离问题大概是 debug 中最重要的核心概念。我们的代码库是由不同的类库、框架组成的,它们有着许多的贡献者,甚至还有一些不再参与项目的人,因此我们的代码库是杂乱无章的。隔离问题可以帮助我们逐步剥离与问题无关的部分以便我们可以把注意力放在解决方案上。 隔离问题的好处包括但不限于以下几条: - 能够弄清楚问题的根本原因是否是我们想的那样,还是存在其它的冲突。 -- 对于时序任务,能判断是否存在竞争状态。 +- 对于时序任务,能判断是否存在时序紊乱。 - 严格审查我们的代码是否还能够更加精简,这样既能帮助我们写代码也能帮助我们维护代码。 - 解开纠缠在一起的代码,以观察到底是只有一个问题还是存在更多的问题。 @@ -27,11 +27,11 @@ 你可以在你代码中写上 `debugger;`,这样你可以看到当时这一小块代码做了什么。 -你在 Chrome 开发者工具中还可以做到更进一步,单步跟踪事件的发生。你也可以用它选择性地观察指定的事件监听器。 +你还可以在 Chrome 开发者工具中进一步进行调试,单步跟踪事件的发生。你也可以用它选择性地观察指定的事件监听器。 ![Step into the next function call](https://cdn.css-tricks.com/wp-content/uploads/2017/04/stepintonextfunctioncall.gif) -古老,好用的 `console.log` 是一种形式的隔离。(PHP 中是 `echo`,python 中是 `print` ……)。你可以一小片一小片地执行代码并对你的假设进行测试,或者检查看有什么东西发生了变化。这可能是最耗费时间的测试方式了。但是无论你的水平如何高,你还是得乖乖用它。ES6 的箭头函数也可以加速我们的 debug 游戏,它让我们可以在控制台中更方便地写单行代码。 +古老,好用的 `console.log` 另一种隔离的方法。(PHP 中是 `echo`,python 中是 `print` ……)。你可以一小片一小片地执行代码并对你的假设进行测试,或者检查看有什么东西发生了变化。这可能是最耗费时间的测试方式了。但是无论你的水平如何高,你还是得乖乖用它。ES6 的箭头函数也可以加速我们的 debug 游戏,它让我们可以在控制台中更方便地写单行代码。 `console.table` 函数也是我最喜欢的工具之一。当你有大量的数据(例如很长的数组、巨大的对象等等)需要展示的时候,它特别有用。`console.dir` 函数也是个不错的选择。它可以把一个对象的属性以可交互的形式展示出来。 @@ -45,7 +45,7 @@ 其实我们或多或少都在这么做。当我们对一个工具越来越熟练时,我们会在没有对设想的情况进行测试的情况下写越来越多的代码。但是当你刚开始用一个语法或技术时,你需要放慢速度并且非常谨慎。你将能越来越快地处理自己无意间造成的错误。其实,当你弄出了一个问题的时候,一次调试一个问题可能会看起来慢一些,但其实要找出哪里发生了变化以及问题的所在是没法快速解决的。我说以上这些话是想告诉你:欲速则不达。 -**你还记得小时候父母告诉你的话吗?“如果你迷路了,待在原地别动。“** 至少我的父母这么说了。这么说的原因是如果他们在到处找我,而我也在到处跑着找他们的话,我们将更难碰到一起。代码也是这样的。你每次动的代码越少就越好,你持续返回的结果越多,就越容易找到问题所在。所以当你在调试时,请尽量不要安装任何东西或者添加新的依赖。如果本应该返回一个静态结果的地方每次都出现不同的错误,你就得特别注意了! +**你还记得小时候父母告诉你的话吗?“如果你迷路了,待在原地别动。“** 至少我的父母这么说了。这么说的原因是如果他们在到处找我,而我也在到处跑着找他们的话,我们将更难碰到一起。代码也是这样的。你每次动的代码越少就越好,你返回一致的结果越多,就越容易找到问题所在。所以当你在调试时,请尽量不要安装任何东西或者添加新的依赖。如果本应该返回一个静态结果的地方每次都出现不同的错误,你就得特别注意了! ### 选用优秀的工具 ### @@ -53,7 +53,7 @@ #### 代码高亮 #### -当然,为你的代码高亮主题找一个最热辣的配色与风格方案是很有趣的,但是请花点时间想清楚这件事。我个人倾向于使用黑色的主题,我的所有代码都会被渲染成明亮的颜色,这样我就可以很轻松地找到错误了。我也尝试过使用 Oceanic Next 配色方案与 Panda 配色方案,但是说实话我还是最喜欢自己的那种。在寻找优秀的代码高亮工具的时候请保持理智,帅气的外观当然很棒,但是为你揪出错误的功能性更加重要。当然,你完全有可能找到两者都很优秀的代码高亮工具。 +当然,为你的代码高亮主题找一个最热辣的配色与风格方案是很有趣的,但是请花点时间想清楚这件事。我通常使用深色主题,当有语法错误时,深色主题会用较亮的颜色显示我的代码,使我能轻松快速地找到错误。我也尝试过使用 Oceanic Next 配色方案与 Panda 配色方案,但是说实话我还是最喜欢自己的那种。在寻找优秀的代码高亮工具的时候请保持理智,帅气的外观当然很棒,但是为你揪出错误的功能性更加重要。当然,你完全有可能找到两者都很优秀的代码高亮工具。 #### 使用 Lint 工具 #### @@ -68,7 +68,7 @@ #### 浏览器插件 #### -插件是真的超级棒,你可以轻松地启用或禁用它们。并且它们能在特定需求中发挥重要的作用。如果你使用一些特定的框架或类库工作,使用它们的开发者工具插件将会带给你无与伦比的便利。不过请注意,插件不仅会降低浏览器的速度,它们也有权限执行代码。因此在你使用之前,请先了解一下插件的作者、评价及背景。总之,下面是一些我最喜欢的插件: +插件是真的超级棒,你可以轻松地启用或禁用它们。并且它们能在特定需求中发挥重要的作用。如果你使用一些特定的框架或类库工作,使用它们的开发者工具插件将会带给你无与伦比的便利。不过请注意,插件不仅会降低浏览器的速度,它们也有权限执行脚本。因此在你使用之前,请先了解一下插件的作者、评价及背景。总之,下面是一些我最喜欢的插件: - Deque Systems 提供的 [aXe](https://chrome.google.com/webstore/detail/axe/lhdoppojpmngadmnindnejefpokejbdd),是一款优秀的可行性分析插件。 - 如果你工作中使用 React,[React DevTools](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi) 是你必不可少的工具,你可以通过它观察虚拟 DOM。 @@ -92,7 +92,7 @@ - [What input](https://ten1seven.github.io/what-input/) 是一个能跟踪当前输入(鼠标、键盘、触摸)与当前信息的实用工具。(感谢 Marcy Sutton 提供了这个便捷的工具) - 如果你做的是响应式开发,或者你得在无数种设备上进行检查,那么 [Ghostlabapp](https://www.vanamco.com/ghostlab/) 是个挺适合你的时髦工具。它为你提供了同步移动 web 开发、测试与检查。 -- [Eruda 是个很棒的工具](http://eruda.liriliri.io/),它可以帮助我们在移动设备上进行调试。我很喜欢它,因为它进一步提供了一个模拟器,还为你准备了控制台和真实的开发者工具,让你更容易理解。 +- [Eruda 是个很棒的工具](http://eruda.liriliri.io/),它可以帮助我们在移动设备上进行调试。我很喜欢它,因为它不仅是一个模拟器,还为你准备了控制台和真实的开发者工具,让你更容易理解。 ![eruda gives you a mobile console](https://cdn.css-tricks.com/wp-content/uploads/2017/04/Screen-Shot-2017-04-10-at-10.38.57-AM.png) From c7c4a143a0290ad3c283acc18a68e92e33d4f102 Mon Sep 17 00:00:00 2001 From: lsvih Date: Thu, 27 Apr 2017 08:46:33 +0800 Subject: [PATCH 276/638] Update es6-modules-support-lands-in-browsers-is-it-time-to-rethink-bundling.md --- ...browsers-is-it-time-to-rethink-bundling.md | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/TODO/es6-modules-support-lands-in-browsers-is-it-time-to-rethink-bundling.md b/TODO/es6-modules-support-lands-in-browsers-is-it-time-to-rethink-bundling.md index c36bb6bbcc2..d5c2d5bed5e 100644 --- a/TODO/es6-modules-support-lands-in-browsers-is-it-time-to-rethink-bundling.md +++ b/TODO/es6-modules-support-lands-in-browsers-is-it-time-to-rethink-bundling.md @@ -2,7 +2,7 @@ > * 原文作者:[Stefan Judis](https://www.contentful.com/about-us/) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 译者:[lsvih](https://github.com/lsvih) -> * 校对者: +> * 校对者:[Aladdin-ADD](https://github.com/Aladdin-ADD),[yzgyyang](https://github.com/yzgyyang) # ES6 模块原生支持在浏览器中落地,是时候该重新考虑打包了吗? # @@ -10,9 +10,9 @@ 最近一段日子,编写高效的 JavaScript 应用变得越来越复杂。早在几年前,大家都还使用脚本连接来减少 HTTP 请求数;后来有了压缩工具,人们为了压缩代码而缩短变量名,甚至连代码的最后一字节都要省出来。 -今天,我们有了 [tree shaking](https://blog.engineyard.com/2016/tree-shaking) 和各种模块打包器,我们为了不在首屏加载时阻塞主进程又开始进行代码分割,[加快交互时间](https://developers.google.com/web/tools/lighthouse/audits/time-to-interactive)。我们还开始移植一切东西:感谢 Babel,让我们能够在现在就使用未来的特性。 +今天,我们有了 [tree shaking](https://blog.engineyard.com/2016/tree-shaking) 和各种模块打包器,我们为了不在首屏加载时阻塞主进程又开始进行代码分割,[加快交互时间](https://developers.google.com/web/tools/lighthouse/audits/time-to-interactive)。我们还开始转译一切东西:感谢 Babel,让我们能够在现在就使用未来的特性。 -ES6 模块由 ECMAScript 标准制定,直到[最近才定稿](http://2ality.com/2014/09/es6-modules-final.html)。社区为它写了很多的文章,讲解如何通过 Babel 使用它们,以及 `import` 和 Node.js 的 `require` 的区别。但是要在浏览器中真正实现它还需要一点时间。我惊喜地发现 Safari 在它的 technology preview 版本中第一个装载了 ES6 模块,并且 Edge 和 Firefox Nightly 版本也将要装载 ES6 模块——虽然目前还不支持。在使用 `RequireJS` 和 `Browserify` 之类的(还记得关于 [AMD 与 CommonJS 的讨论吗](https://addyosmani.com/writing-modular-js/)?)工具后,模块化支持似乎最终抵达了浏览器。让我们来看看明朗的未来带来了怎样的礼物吧!🎉 +ES6 模块由 ECMAScript 标准制定,[定稿有些时日了](http://2ality.com/2014/09/es6-modules-final.html)。社区为它写了很多的文章,讲解如何通过 Babel 使用它们,以及 `import` 和 Node.js 的 `require` 的区别。但是要在浏览器中真正实现它还需要一点时间。我惊喜地发现 Safari 在它的 technology preview 版本中第一个装载了 ES6 模块,并且 Edge 和 Firefox Nightly 版本也将要支持 ES6 模块——虽然目前还不支持。在使用 `RequireJS` 和 `Browserify` 之类的(还记得关于 [AMD 与 CommonJS 的讨论吗](https://addyosmani.com/writing-modular-js/)?)工具后,浏览器终于能支持模块了。让我们来看看明朗的未来带来了怎样的礼物吧!🎉 ## 传统方法 ## @@ -58,7 +58,7 @@ export default function() { } ``` -这个 app 将会显示“Hello world”,以此告诉我们文件加载完成。 +这个 app 将会显示“Hello world”。在下文中显示“Hello world” 即表示脚本加载成功。 ### 装载一个代码包(bundle) ### @@ -170,11 +170,11 @@ import dep1 from './dep-1.js'; 这儿有几个问题。首先,JavaScript 在 ES6 模块中运行与平常在 script 元素中不同。Axel Rauschmayer 在[他的探索 ES6 一书](http://exploringjs.com/es6/ch_modules.html#sec_modules-vs-scripts)中很好地讨论了这个问题。我推荐你点击上面的链接阅读这本书,但是在此我先快速地总结一下主要的不同点: - ES6 模块默认在严格模式下运行(因此你不需要加上 `use strict` 了)。 -- `this` 指向的最高一级对象是 `undefined`(而不是 window)。 +- 最外层的 `this` 指向 `undefined`(而不是 window)。 - 最高级变量是 module 的局部变量(而不是 global)。 - ES6 模块会在浏览器完成 HTML 的分析之后异步加载与执行。 -我认为,这些特性是巨大进步。模块是局部的——这意味着我们不再需要到处使用 IIFE 了,而且我们不用再担心全局变量泄露。而且默认在严格模式下运行,意味着我们可以在很多地方抛弃 `use strict` 声明。 +我认为,这些特性是巨大进步。模块是局部的——这意味着我们不再需要到处使用 IIFE 了,而且我们不用再担心全局变量泄露。而且默认在严格模式下运行,意味着我们可以在很多地方抛弃 `use strict` 声明。 > 译注:IIFE 全称 immediately-invoked function expression,即立即执行函数,也就是大家熟知的在函数后面加括号。 @@ -197,13 +197,13 @@ import dep1 from './dep-1.js'; ``` -如果你想详细了解这方面内容,可以阅读 [script 元素说明书](https://html.spec.whatwg.org/multipage/scripting.html#the-script-element),这篇文章简单易读,并且包含了一些示例。 +如果你想详细了解这方面内容,可以阅读 [script 元素说明](https://html.spec.whatwg.org/multipage/scripting.html#the-script-element),这篇文章简单易读,并且包含了一些示例。 ## 压缩纯 ES6 代码 ## 还没完!我们现在能为 Chrome 提供压缩过的代码包,但是还不能为 Safari Preview 提供单独压缩过的文件。我们如何让这些文件变得更小呢?UglifyJS 能完成这项任务吗? -然而必须指出,UglifyJS 并不能完全处理好 ES6 代码。虽然它有个 `harmony` 分支([地址](https://github.com/mishoo/UglifyJS2/tree/harmony))支持ES6,但不幸的是在我写这 3 个 JavaScript 文件的时候它并不能正常工作。 +然而必须指出,UglifyJS 并不能完全处理好 ES6 代码。虽然它有个 `harmony` 开发版分支([地址](https://github.com/mishoo/UglifyJS2/tree/harmony))支持ES6,但不幸的是在我写这 3 个 JavaScript 文件的时候它并不能正常工作。 ``` $ uglifyjs dep-1.js -o dep-1.min.js @@ -260,7 +260,7 @@ $ ll dist/modules 对单个 JS 文件进行压缩取得了很好的效果。文件大小从 856B 降低到了 298B,但是我们还能进一步地加快加载速度。通过使用 ES6 模块,我们可以装载更少的代码,但是看看瀑布图你会发现,request 会按照模块的依赖链一个一个连续地加载。 -那如果我们像之前在浏览器中对代码进行预加载那样,用 `` 元素告知浏览器要加载额外的 request,是否会加快模块的加载速度呢?在 Webpack 中,我们已经有了类似的工具,比如 Addy Osmani 的 [Webpack 预加载插件](https://github.com/GoogleChrome/preload-webpack-plugin)可以对分割的代码进行预加载,那 ES6 模块有没有类似的方法呢?如果你还不清楚 `rel="preload"` 是如何运作的,你可以先阅读 Yoav Weiss 在 Smashing Magazine 发表的相关文章:[点击阅读](https://www.smashingmagazine.com/2016/02/preload-what-is-it-good-for/) +那如果我们像之前在浏览器中对代码进行预加载那样,用 `` 元素告知浏览器要加载额外的 request,是否会加快模块的加载速度呢?在 Webpack 中,我们已经有了类似的工具,比如 Addy Osmani 的 [Webpack 预加载插件](https://github.com/GoogleChrome/preload-webpack-plugin)可以对分割的代码进行预加载,那 ES6 模块有没有类似的方法呢?如果你还不清楚 `rel="preload"` 是如何运作的,你可以先阅读 Yoav Weiss 在 Smashing Magazine 发表的相关文章:[点击阅读](https://www.smashingmagazine.com/2016/02/preload-what-is-it-good-for/) 但是,ES6 模块的预加载并不是那么简单,他们与普通的脚本有很大的不同。那么问题来了,对一个 link 元素加上 `rel="preload"` 将会怎样处理 ES6 模块呢?它也会取出所有的依赖文件吗?这个问题显而易见(可以),但是使用 `preload` 命令加载模块,需要解决更多浏览器的内部实现问题。[Domenic Denicola](https://twitter.com/domenic) 在[一个 GitHub issue](https://github.com/whatwg/fetch/issues/486) 中讨论了这方面的问题,如果你感兴趣的话可以点进去看一看。但是事实证明,使用 `rel="preload"` 加载脚本与加载 ES6 模块是截然不同的。可能以后最终的解决方案是用另一个 `rel="modulepreload"` 命令来专门加载模块。在本文写作时,[这个 pull request](https://github.com/whatwg/html/pull/2383) 还在审核中,你可以点进去看看未来我们可能会怎样进行模块的预加载。 @@ -297,7 +297,7 @@ document.body.appendChild(getComponent()); ### 压缩工作仅对大文件表现良好 ### -如果你仔细看上面 Safari 开发者工具的截图,你可能会注意到转换后的文件大小其实比源码还要大。在很大的 JavaScript app 中这个现象会更加明显,一堆的小 Chunk 会造成文件大小的很大不同,因为 GZIP 并不能很好地压缩小文件。 +如果你仔细看上面 Safari 开发者工具的截图,你可能会注意到传输后的文件大小其实比源码还要大。在很大的 JavaScript app 中这个现象会更加明显,一堆的小 Chunk 会造成文件大小的很大不同,因为 GZIP 并不能很好地压缩小文件。 Khan Academy 在前一段时间[探究了同样的问题](http://engineering.khanacademy.org/posts/js-packaging-http2.htm),他是用 HTTP/2 进行研究的。装载更小的文件能够很好地确保缓存命中率,但到最后它一般都会作为一个权衡方案,而且它的效果会被很多因素影响。对于一个很大的代码库来说,分解成若干个 chunk(一个 *vendor* 文件和一个 app bundle)是理所当然的,但是要装载数千个不能被压缩的小文件可能并不是一种明智的方法。 @@ -325,7 +325,7 @@ ES6 模块即将到来,但是直到它最终在各大主流浏览器中实现 不要把所有东西都进行分割然后就假设它会改善性能。我们即将迎来 ES6 模块的浏览器原生支持,但是这不意味着我们可以抛弃构建过程与合适的打包策略。在我们 Contentful 这儿,将继续坚持我们的构建过程,以及继续使用我们的 [JavaScript SDKs](https://www.contentful.com/developers/docs/javascript/) 进行打包。 -然而,我们必须承认现在前端的开发体验仍然良好。JavaScript 仍在进步,最终我们将能使用真正融入语言的模块。在几年后,原生模块对 JavaScript 生态的影响以及最佳实践方法将会是怎样的呢?让我们拭目以待。 +然而,我们必须承认现在前端的开发体验仍然良好。JavaScript 仍在进步,最终我们将能够使用语言本身提供的模块系统。在几年后,原生模块对 JavaScript 生态的影响以及最佳实践方法将会是怎样的呢?让我们拭目以待。 ## 其它资源 ## From 8a1b37539029d249bec77be9705b1d4ee1902e2f Mon Sep 17 00:00:00 2001 From: lsvih Date: Thu, 27 Apr 2017 09:42:03 +0800 Subject: [PATCH 277/638] Update debugging-tips-tricks.md --- TODO/debugging-tips-tricks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO/debugging-tips-tricks.md b/TODO/debugging-tips-tricks.md index 0c8afa5cdbe..3f1843c1112 100644 --- a/TODO/debugging-tips-tricks.md +++ b/TODO/debugging-tips-tricks.md @@ -31,7 +31,7 @@ ![Step into the next function call](https://cdn.css-tricks.com/wp-content/uploads/2017/04/stepintonextfunctioncall.gif) -古老,好用的 `console.log` 另一种隔离的方法。(PHP 中是 `echo`,python 中是 `print` ……)。你可以一小片一小片地执行代码并对你的假设进行测试,或者检查看有什么东西发生了变化。这可能是最耗费时间的测试方式了。但是无论你的水平如何高,你还是得乖乖用它。ES6 的箭头函数也可以加速我们的 debug 游戏,它让我们可以在控制台中更方便地写单行代码。 +古老,好用的 `console.log` 是另一种隔离的方法。(PHP 中是 `echo`,python 中是 `print` ……)。你可以一小片一小片地执行代码并对你的假设进行测试,或者检查看有什么东西发生了变化。这可能是最耗费时间的测试方式了。但是无论你的水平如何高,你还是得乖乖用它。ES6 的箭头函数也可以加速我们的 debug 游戏,它让我们可以在控制台中更方便地写单行代码。 `console.table` 函数也是我最喜欢的工具之一。当你有大量的数据(例如很长的数组、巨大的对象等等)需要展示的时候,它特别有用。`console.dir` 函数也是个不错的选择。它可以把一个对象的属性以可交互的形式展示出来。 From 81aee24492ce655d623d42d830783c2353a9a05e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=B9=E5=8F=B7=E4=B8=89?= Date: Thu, 27 Apr 2017 10:10:33 +0800 Subject: [PATCH 278/638] =?UTF-8?q?=E6=9B=B4=E6=8D=A2=E5=9B=BE=E7=89=87?= =?UTF-8?q?=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO/a-case-for-using-storyboards-on-ios.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TODO/a-case-for-using-storyboards-on-ios.md b/TODO/a-case-for-using-storyboards-on-ios.md index 9c29d17904d..36b67970b7b 100644 --- a/TODO/a-case-for-using-storyboards-on-ios.md +++ b/TODO/a-case-for-using-storyboards-on-ios.md @@ -136,9 +136,9 @@ So for those of you who want the benefits or storyboards, but want to minimize t *If you liked this one, check out some some other articles from my team:* -[![Markdown](http://i4.buimg.com/1949/a68c5bee9aecbe65.png)](https://medium.cobeisfresh.com/how-to-win-a-hackathon-tips-tricks-8cd391e18705) +[![](http://ww3.sinaimg.cn/large/006tNbRwgy1ff10iqm6lxj315a0ai76b.jpg)](https://medium.cobeisfresh.com/how-to-win-a-hackathon-tips-tricks-8cd391e18705) -[![Markdown](http://i4.buimg.com/1949/d5fb464e8b33d195.png)](https://medium.cobeisfresh.com/accessing-types-from-extensions-in-swift-32ca87ec5190) +[![](http://ww4.sinaimg.cn/large/006tNbRwgy1ff10jmsrsej314c0aa0ud.jpg)](https://medium.cobeisfresh.com/accessing-types-from-extensions-in-swift-32ca87ec5190) From c1d4ced9e78f33e7e1cd19b2e158d217799b0fde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=B9=E5=8F=B7=E4=B8=89?= Date: Thu, 27 Apr 2017 23:21:47 +0800 Subject: [PATCH 279/638] Create project-need-react.md --- TODO/project-need-react.md | 120 +++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 TODO/project-need-react.md diff --git a/TODO/project-need-react.md b/TODO/project-need-react.md new file mode 100644 index 00000000000..f861a660328 --- /dev/null +++ b/TODO/project-need-react.md @@ -0,0 +1,120 @@ +> * 原文地址:[When Does a Project Need React?](https://css-tricks.com/project-need-react/) +> * 原文作者:[CHRIS COYIER](https://css-tricks.com/author/chriscoyier/) +> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) +> * 译者: +> * 校对者: + +# When Does a Project Need React? # + +You know when a project needs HTML and CSS, because it's all of them. When you reach for JavaScript is fairly clear: when you need interactivity or some functionality that only JavaScript can provide. It used to be fairly clear when we reached for libraries. We reached for jQuery to help us simplify working with the DOM, Ajax, and handle cross-browser issues with JavaScript. We reached for underscore to give us helper functions that the JavaScript alone didn't have. + +As the need for these libraries fades, and we see a massive rise in new frameworks, I'd argue it's not as clear **when to reach for them**. At what point do we need React? + +I'm just going to use React as a placeholder here for kinda large JavaScript framework thingies. Vue, Ember, Svelte... whatever. I understand they aren't all the same, but when to reach for them I find equally nebulous. + +Here's my take. + +### ✅ Because there is lots of state. ### + +Even "state" is a bit of a nebulous word. Imagine things like this: + +- Which navigation item is active +- Whether a button is disabled or not +- The value of an input +- Which accordion sections are expanded +- When an area is loading +- The user that is logged in and the team they belong to +- Whether the thing the user is working on is published, or a draft + +"Business logic"-type stuff that we regularly deal with. State can also be straight up content: + +- All the comments on an article and the bits and bobs that make them up +- The currently viewed article and all its metadata +- An array of related articles and the metadata for those +- A list of authors +- An an activity log of recent actions a user has taken + +React doesn't help you *organize* that state, it just says: I know you need to *deal* with state, so let's just *call* it state and have programmatic ways to set and get that state. + +Before React, we might have *thought* in terms of state, but, for the most part, didn't manage it as a direct concept. + +Perhaps you've heard the phrase "single source of truth"? A lot of times we treated the DOM as our single source of truth. For example, say you need to know if a form on your website is able to be submitted. Maybe you'd check to see if `$(".form input[type='submit']).is(":disabled")` because all your business logic that dealt with whether or not the form could be submitted or not ultimately changed the disabled attribute of that button. So the button became this defacto source of truth for the state of your app. + +Or say you needed to figure of the name of the first comment author on an article. Maybe you'd write `$(".comments > ul > li:first > h3.comment-author).text()` because the DOM is the only place that knows that information. + +React kinda tells us: + +1. Let's start thinking about all that stuff as state. +2. I'll do ya one better: state is a chunk of JSON, so it's easy to work with and probably works nicely with your back end. +3. And one more even better: You build your HTML using bits of that state, and you won't have to deal with the DOM directly at all, I'll handle all that for you (and likely do a better/faster job than you would have.) + +### ✅ To Fight Spaghetti. ### + +This is highly related to the state stuff we were just talking about. + +"Spaghetti" code is when code organization and structure has gotten away from you. Imagine, again, a form on your site. It has some business logic stuff that specifically deals with the inputs inside of it. Perhaps there is a number input that, when changed, display the result of some calculation beside it. The form can also be submitted and needs to be validated, so perhaps that code is in a validation library elsewhere. Perhaps you disable the form until you're sure all JavaScript has loaded elsewhere, and that logic is elsewhere. Perhaps when the form is submitted, you get data back and that needs logic and handling. Nothing terribly surprising here, but you can see how this can get confusing quickly. How does a new dev on the project, looking at that form, reason out everything that is going on? + +React encourages the use of building things into modules. So this form would likely either be a module of its own or comprised of other smaller modules. Each of them would handle the logic that is directly relevant to it. + +React says: *well, you aren't going to be watching the DOM directly for changes and stuff, because the DOM is mine and you don't get to work with it directly*. Why don't you start thinking of these things as part of the state, change state when you need to, and I'll deal with the rest, rerendering what needs to be rerendered. + +It should be said that React itself doesn't entirely solve spaghetti. You can still have state in all kinds of weird places, name things badly, and connect things in weird ways. + +In my limited experience, it's Redux that is the thing that really kills spaghetti. Redux says: I'll handle *all* the important state, totally globally, not module-by-module. I am the absolute source of truth. If you need to change state, there is quite a *ceremony* involved (I've heard it called that, and I like it.) There are reducers and dispatched actions and such. All changes follow the ceremony. + +If you go the Redux road (and there are variations of it, of course), you end up with really solid code. It's much harder to break things and there are clear trails to follow for how everything is wired together. + +### ✅ Lots of DOM management. ### + +Manually handling the DOM is probably the biggest cause of spaghetti code. + +1. Inject HTML over here! +2. Rip something out over here! +3. Watch this area for this event! +4. Bind a new event over here! +5. New incoming content! Inject again! Make sure it has the right event bindings! + +All these things can happen any time from anywhere in an app that's gone spaghetti. Real organization has been given up and it's back to the DOM as the source of truth. It's hard to know exactly what's going on for any given element, so everybody just asks the DOM, does what they need to do, and crosses their fingers it doesn't mess with somebody else. + +React says: you don't get to deal with the DOM directly. I have a virtual DOM and I deal with that. Events are bound directly to the elements, and if you need it to do something above and beyond something directly handle-able in this module, you can kind of ceremoniously call things in higher order modules, but that way, the breadcrumb trail can be followed. + +*Complicated* DOM management is another thing. Imagine a chat app. New chat messages might appear because a realtime database has new data from other chatters and some new messages have arrives. Or you've typed a new message yourself! Or the page is loading for the first time and old messages are being pulled from a local data store so you have something to see right away. Here's [a Twitter thread](https://twitter.com/mjackson/status/849636985740210177) that drives that home. + +### ❌ Just because. It's the new hotness. ### + +Learning something for the sake of learning something is awesome. Do that. + +Building a project for clients and real human being users requires more careful consideration. + +A blog, for example, *probably* has none of the problems and fits none of the scenarios that would make React a good fit. And because it's not a good fit, it's probably a *bad* fit, because it introduces complicated technology and dependencies for something that doesn't call for it. + +And yet, gray area. If that blog is a SPA ("Single Page App", e.g. no browser refreshing) that is built from data from a headless CMS and had fancy server-side rendering... well maybe that is React territory again. + +The web app CMS that makes that blog? Maybe a good choice for React, because of all the state. + +### ❌ I just like JavaScript and want to write everything in JavaScript. ### + +People get told, heck, I've told people: learn JavaScript. It's huge. It powers all kinds of stuff. There are jobs in it. It's not going anyway. + +It's only in recent web history that it's become possible to never leave JavaScript. You got Node.js on the server side. There are loads of projects that yank CSS out of the mix and handle styles through JavaScript. And with React, your HTML is in JavaScript too. + +All JavaScript! All hail JavaScript! + +That's cool and all, but again, just because you can doesn't mean you should. Not all projects call for this, and in fact, most probably don't. + +### ☯️ That's what I know. ### + +(There are decent emojis for YES and NO, but MAYBE is tougher!) + +You're learning. Awesome. Everybody is. Keep learning. The more you know the more informed decisions you can make about what tech to use. + +But sometimes you gotta build with what you know, so I ain't gonna ding ya for that. + +### ☯️ That's where the jobs are. ### + +Not everybody has a direct say in what technology is used on any given project. Hopefully, over time, you have influence in that, but that takes time. Eden says she [spent 2 years with Ember](https://twitter.com/edenthecat/status/849640183360352257) because that's where the jobs were. No harm in that. Everybody's gotta get paid, and Ember might have been a perfect fit for those projects. + + +--- + +> [掘金翻译计划](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)、[React](https://github.com/xitu/gold-miner#react)、[前端](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)。 From e7fdfb44155c61b3700874cde1b5f7e6c9ef38ee Mon Sep 17 00:00:00 2001 From: zhuzi Date: Thu, 27 Apr 2017 23:44:07 +0800 Subject: [PATCH 280/638] translating --- ...t-the-future-of-component-based-styling.md | 51 ++++++++++++------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/TODO/css-in-javascript-the-future-of-component-based-styling.md b/TODO/css-in-javascript-the-future-of-component-based-styling.md index 9da5e81bf28..ef3c032d366 100644 --- a/TODO/css-in-javascript-the-future-of-component-based-styling.md +++ b/TODO/css-in-javascript-the-future-of-component-based-styling.md @@ -1,30 +1,40 @@ > * 原文地址:[CSS in JavaScript: The future of component-based styling](https://medium.freecodecamp.com/css-in-javascript-the-future-of-component-based-styling-70b161a79a32) > * 原文作者:[Jonathan Z. White](https://medium.freecodecamp.com/@JonathanZWhite) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 译者: +> * 译者:[bambooom](https://github.com/bambooom) > * 校对者: -# CSS in JavaScript: The future of component-based styling # +# JavaScript 中的 CSS:基于组件的样式的未来 ![](https://cdn-images-1.medium.com/max/1000/1*yVKDbwtvfoakj3RZ9g8ARQ.png) -Image by [@jonathanzwhite](https://twitter.com/JonathanZWhite) +图片所属 [@jonathanzwhite](https://twitter.com/JonathanZWhite) -By adopting inline styles, we can get all of the programmatic affordances of JavaScript.This gives us the benefits of something like a CSS pre-processor (variables, mixins, and functions). It also solves a lot of the problems that CSS has, such as global namespacing and styling conflicts. +> By adopting inline styles, we can get all of the programmatic affordances of JavaScript.This gives us the benefits of something like a CSS pre-processor (variables, mixins, and functions). It also solves a lot of the problems that CSS has, such as global namespacing and styling conflicts. -For a deep dive into the problems that CSS in JavaScript solves, check out the famous presentation: [React CSS in JS](https://speakerdeck.com/vjeux/react-css-in-js). For a case study on the performance improvements you get from Aphrodite, you can read [Inline CSS at Khan Academy: Aphrodite](http://engineering.khanacademy.org/posts/aphrodite-inline-css.htm). If you want to learn more about CSS in JavaScript best practices, check out [Airbnb’s styleguide](https://github.com/airbnb/javascript/tree/master/css-in-javascript). +使用行内样式使我们可以获得 JavaScript 的所有编程支持。这给了我们类似 CSS 预处理器(变量、混入和函数)的好处。它也解决了 CSS 的很多问题,如全局命名空间和样式冲突。 -In addition we’ll be using inline JavaScript styles to build components to address some of the fundamentals of design I covered in one of my previous articles: [Before you can master design, you must first master the fundamentals](https://medium.freecodecamp.com/before-you-can-master-design-you-must-first-master-the-fundamentals-1981a2af1fda). +> For a deep dive into the problems that CSS in JavaScript solves, check out the famous presentation: [React CSS in JS](https://speakerdeck.com/vjeux/react-css-in-js). For a case study on the performance improvements you get from Aphrodite, you can read [Inline CSS at Khan Academy: Aphrodite](http://engineering.khanacademy.org/posts/aphrodite-inline-css.htm). If you want to learn more about CSS in JavaScript best practices, check out [Airbnb’s styleguide](https://github.com/airbnb/javascript/tree/master/css-in-javascript). -### A motivating example ### +如果想要更深入了解 JavaScript 中的 CSS 所解决的问题,可以查看著名的演示幻灯:[React CSS in JS](https://speakerdeck.com/vjeux/react-css-in-js)。有关使用 Aphrodite 性能优化的案例研究,你可以阅读 [行内 CSS 在可汗学院:Aphrodite](http://engineering.khanacademy.org/posts/aphrodite-inline-css.htm)。如果你想了解更多,可以阅读 [Airbnb 的风格指南](https://github.com/airbnb/javascript/tree/master/css-in-javascript). -Let’s start off with a simple example: creating and styling a button. +> In addition we’ll be using inline JavaScript styles to build components to address some of the fundamentals of design I covered in one of my previous articles: [Before you can master design, you must first master the fundamentals](https://medium.freecodecamp.com/before-you-can-master-design-you-must-first-master-the-fundamentals-1981a2af1fda). -Normally the component and its associated styles would go in the same file: `Button` and `ButtonStyles`. This is because they fall under the same concern: the view. However, for this example, I broke up the code into multiple gists to make it more digestible. +此外,我们将使用行内 JavaScript 样式来构建组件,以解决我之前的一篇文章([掌握设计之前,必须掌握基本原理](https://medium.freecodecamp.com/before-you-can-master-design-you-must-first-master-the-fundamentals-1981a2af1fda))中涉及的一些设计基础问题。 -Here’s the button component: +### 一个启发性的例子 ### -``` +> Let’s start off with a simple example: creating and styling a button. + +让我们从一个简单的例子开始,构建一个按钮并给它添加样式。 + +> Normally the component and its associated styles would go in the same file: `Button` and `ButtonStyles`. This is because they fall under the same concern: the view. However, for this example, I broke up the code into multiple gists to make it more digestible. + +一般来说,组件及其样式在同一个文件中:`Button` 和 `ButtonStyles`。这是因为他们都属于视图层。但是,下面的例子中,我将代码拆分成多个 gist,以便更容易理解。 + +下面就是按钮组件: + +```javascript ... function Button(props) { @@ -38,11 +48,13 @@ function Button(props) { } ``` -This is nothing unexpected — just a stateless React component. Where Aphrodite comes into play is in the `className` property. The function `css` takes in a `styles` object and converts it into css. The `styles` object is created with Aphrodite’s `StyleSheet.create({ ... })` function. You can see the output of `StyleSheet.create({ ... })` with this [Aphrodite playground](https://output.jsbin.com/qoseye?). +> This is nothing unexpected — just a stateless React component. Where Aphrodite comes into play is in the `className` property. The function `css` takes in a `styles` object and converts it into css. The `styles` object is created with Aphrodite’s `StyleSheet.create({ ... })` function. You can see the output of `StyleSheet.create({ ... })` with this [Aphrodite playground](https://output.jsbin.com/qoseye?). -**Here is the button stylesheet:** +这没什么特别的,只是一个无状态的 React 组件。Aphrodite 起作用的地方是在 `className` 属性中。`css` 函数接受一个 `styles` 对象为参数并将其转换为 `css`。`styles` 对象是由 Aphrodite 的函数 `StyleSheet.create({ ... })` 创建的,你可以用 [Aphrodite playground](https://output.jsbin.com/qoseye?) 来看下这个函数的输出结果。 -``` +**下面是按钮的样式表:** + +```javascript ... const gradient = 'linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)'; @@ -61,16 +73,17 @@ const styles = StyleSheet.create({ }); ``` -One of the benefits of Aphrodite is that migration is straightforward and the learning curve is low. Properties like `border-radius` become `borderRadius` and values become strings. Pseudo-selectors, media queries, and font definitions all work. In addition, vendor prefixes are added automatically. +> One of the benefits of Aphrodite is that migration is straightforward and the learning curve is low. Properties like `border-radius` become `borderRadius` and values become strings. Pseudo-selectors, media queries, and font definitions all work. In addition, vendor prefixes are added automatically. -**Here is the result:** +Aphrodite 的优势之一就是迁移是很直观的,学习曲线很平缓。类似 `border-radius` 变成 `borderRadius`,值变成字符串。伪类选择器、媒体查询、字体定义都可以正常工作。另外也自动添加浏览器引擎前缀。 -![](https://cdn-images-1.medium.com/max/800/1*x1ccRv9UGvcxBvz4TvC4Qg.png) +**下面就是按钮的样子:** -[One of the benefits of Aphrodite is that migration is straightforward and the learning curve is low.](https://twitter.com/JonathanZWhite) +![](https://cdn-images-1.medium.com/max/800/1*x1ccRv9UGvcxBvz4TvC4Qg.png) -With this example in mind, **let’s take a look at how we can use Aphrodite to build a basic visual design system**, focusing on the following design fundamentals: typography and spacing. +> With this example in mind, **let’s take a look at how we can use Aphrodite to build a basic visual design system**, focusing on the following design fundamentals: typography and spacing. +以这个例子为基础,**让我们来看看我们如何使用 Aphrodite 来构建一个基本的视觉设计系统**,着重关注字体排版和间距两个设计基础元素。 ### Fundamental №1 —Typography ### From 3169c551bc31cf26e6fdd12ecc3e4bfb6c1862a5 Mon Sep 17 00:00:00 2001 From: ylq167 Date: Fri, 28 Apr 2017 15:48:42 +0800 Subject: [PATCH 281/638] =?UTF-8?q?=E5=88=9D=E7=A8=BF=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO/everyone-is-a-designer-get-over-it.md | 35 +++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/TODO/everyone-is-a-designer-get-over-it.md b/TODO/everyone-is-a-designer-get-over-it.md index aee8b2663cd..3d731931f0d 100644 --- a/TODO/everyone-is-a-designer-get-over-it.md +++ b/TODO/everyone-is-a-designer-get-over-it.md @@ -8,78 +8,111 @@ ![](https://cdn-images-1.medium.com/max/2000/1*xIoFsnWI_2-1VOy00a2KrQ.jpeg) Photo by Alice Achterhof [via Unsplash](https://unsplash.com/search/designer-paint?photo=FwF_fKj5tBo) +图片作者 Alice Achterhof [来自于 Unsplash](https://unsplash.com/search/designer-paint?photo=FwF_fKj5tBo) # Everyone is a designer. Get over it. # +# 人人都是设计师。忘了它吧。# Recently, [Jared Spool](https://www.uie.com/about/) caught my attention with [an article](https://articles.uie.com/signup/) about how Netflix’s performance engineers are *actually designers*. It’s a provocative idea, but it makes sense. His argument is that everyone in your organization (including performance engineers) designs the product, not just the people with “design” in their job titles. +最近,[Jared Spool](https://www.uie.com/about/) 通过一篇关于 Netflix 的性能工程师竟然是**设计师**的[文章](https://articles.uie.com/signup/)吸引了我的注意 。这是一个挑衅的想法,但也有一定的道理。他的论点是你团队中的每个人(包括性能工程师)都参与到产品设计,而不仅仅是职位为「设计」的人。 ![](https://cdn-images-1.medium.com/max/800/1*qLoczEHONP188zelJbn-6w@2x.png) From some of the reactions, you might think Jared had kidnapped someone’s baby for ritual sacrifice. What exactly did Jared write? +从一些反应中,你可能会认为 Jared 绑架了一些人的宝贝用来献祭。Jared 到底写了什么呢? > The members of this team are performance engineers. They are architecting, engineering, and maintaining the performance of a very complex system. It occupies all their time and then some. In systems engineering, there are few jobs more technical than these. +> 这个团队的成员都是性能工程师。他们架设,开发,维护一个复杂系统的性能。这占据了他们所有的时间。而且还远不止如此,在系统工程方面,几乎没有比这更技术性的工作。 > And yet, at the very moment that a Netflix viewer’s video stream stops and that spinning animation appears, indicating the player is now awaiting more data, these engineers make a dramatic change. **They become user experience designers.** +> 然而,在 Netflix 观众的视频流停止并出现旋转动画的那一刻,表明播放器正在等待更多的数据,这些工程师发生了戏剧性的转变,**他们变成了用户体验设计师。** I made that last sentence bold — because it’s really important. Some designers are uncomfortable with the idea that an engineer or a salesperson or a CFO could be a *designer*. +我把最后一句话加粗了,因为这真的很重要。一些设计师对工程师或销售人员或财务官可能是「设计师」的想法感到不适。 ![](https://cdn-images-1.medium.com/max/800/1*ErZDaGRy3mJ19jGdWqeJgA@2x.png) Common reactions +常见的反应 Whether you like it or not, whether you approve it or not, people outside of your design team are making significant *design* choices that affect your customers in important ways. They are *designing* your product. They are *designers*. +不管你是不是喜欢,也不管你是不是同意,设计团队以外的人都会以重要的方式通过重大的**设计**决策来影响你的客户。 This shouldn’t be provocative — it’s just a statement of fact. I work with[ dozens of startups](http://www.gv.com/portfolio/) every year, and I see it happen at every one. A CFO makes a pricing decision and changes the product experience. An engineer makes a performance trade-off. A salesperson writes a script for talking to customers. In my view, people who fundamentally change the customer’s experience are *designers*. +这不应该是**挑衅**-这只是一个事实的陈述。我每年与几十家[创业公司](http://www.gv.com/portfolio/)合作,我看到每一家公司都会发生这种情况。首席财务官作出定价决定改变了产品体验。一个工程师做了性能权衡。销售人员写一个与客户交谈的脚本。在我看来,从根本上改变用户体验的人就可以叫做**设计师**。 If this is so self-evident, why do Jared and I press the point? I keep beating the drum because I want designers to change the way they think about their role and become better stewards of good design. +如果这是不言而喻的,那为什么 Jared 和我坚持这一点?我会持续宣传(此处翻译是否合适?),因为我希望设计师改变他们对自身角色的看法,并成为更好的设计师。 For a moment, consider how this shift in perspective could change the way you work. +现在,想想这种观点的改变将如何改变你的工作方式。 #### Everyone needs a design mindset #### +#### 每个人都需要设计思维 #### When you accept the reality that design decisions are coming from outside your group, by people without “design” in their job titles, you approach your co-workers differently . Now they’re not just your co-workers — they’re your design team. +当你接受设计决策来自于你团队之外而且职位不是「设计」的人的现实时,你就会向你的其他同事靠拢。现在他们不仅是你的同事-他们还是你的设计团队。 The companies that produce great design, such as Apple and Airbnb, have learned this. Alex Schleifer, VP of design at Airbnb,[ tells *Wired*](https://www.wired.com/2015/01/airbnbs-new-head-design-believes-design-led-companies-dont-work/) how the company *isn’t* design-led: +创造出顶尖设计的公司,像苹果(Apple)和爱彼迎(Airbnb),都已经意识到了这一点。 爱彼迎(Airbnb)的设计副总裁 Alex Schleifer [告诉**《连线》杂志**](https://www.wired.com/2015/01/airbnbs-new-head-design-believes-design-led-companies-dont-work/)公司如何才能**不由**设计主导。 > *The solution [at Airbnb] actually deemphasizes the designers. The point… isn’t to create a “design-led culture,” because that tends to tell anyone who isn’t a designer that their insights take a backseat. It puts the entire organization in the position of having to react to one privileged point of view. Instead, Schleifer wants more people to appreciate what typically lies only within the realm of designers — the user viewpoint.* +> **爱彼迎(Airbnb)的解决方法实际上不强调设计师,这一点,不是要创造一个「设计主导的文化」,而是因为这往往会告诉任何一个不是设计师的人他们的洞察力是不重要的。它使整个组织能够对一个特别的观点作出反应。(此处该如何翻译?)相反,Schleifer 希望更多的人意识到用户视角通常只在设计师的领域。**(此处该如何翻译?) Does everyone need all the skills of a designer? Of course not. But each person needs to be armed with the tools to understand how their decisions affect the customer experience. +每个人都需要掌握设计师的所有技能吗?当然不是。但是每个人都需要配备工具来理解他们的决定是如何影响用户体验的。 When an engineer takes a shortcut and scrimps on performance, they need to understand how that damages the user experience. Likewise, when a designer pushes an engineer to make a change that affects performance, that engineer should help the designer make the best overall design decision — not just roll over and do what the designer asked. It’s this type of respectful collaboration that makes great design happen. +当一个工程师在性能方面采用敏捷的途径时,他们需要理解这是如何损害用户体验的。同样,当设计师需要推动工程师做影响性能的改变时,这个工程师应该帮助设计师做出最好的整体设计决策,而不仅是否定设计师所需要的。正是这种尊重的合作创造出伟大的设计。 One of the best ways to encourage empathy is to watch customer research studies with co-workers from across your company. When my colleague Michael Margolis runs a study with a GV company, we insist that the real team — not just the designers — watch those interviews and take notes. If it’s not possible for everyone to watch in real time, you can record the sessions and schedule a “viewing party” for later. +激发共鸣的最好方式就是和来自公司的同事一起看用户调研。当我的同事 Michael Margolis 和 GV 公司一起进行研究时,我们坚持认为,真正的团队-不仅仅是设计师-需要看这些访谈并进行记录。如果不可能做到每个人都全部观看,你可以记录会话并在之后安排一个「观看聚会」。 #### Work outside your design team #### +#### 在设计团队之外工作 #### When you accept that design happens almost everywhere in your organization, you have to take responsibility for it. Your app is slow? Go sit with your engineering team. Your marketing team is poorly communicating your product to future customers? You’d better offer to work with them on the problem. +当你接受设计这个行为发生在你的团队中几乎任何地方时,你必须承担责任。你的应用很慢?和你的工程团队坐在一起。你的营销团队很难将你的产品传达给未来的客户?你最好在这个问题上和他们一起工作。 Yes, doing design with everyone at your company is a lot of work. But it’s necessary if you want to be a truly great designer — otherwise, you’re simply papering over bad decisions. For example, imagine that your CEO created a complex pricing structure for your product. You could focus on making the pricing page as clear as possible using your interface and information design skills. But the harder and more important design opportunity is to work with your CEO on repricing your product so it’s clear to customers and compatible with the business goals. +是的,和你公司里的每个人一起做设计的工作量很大。但是,如果你想成为一名真正伟大的设计师,那么这是必要的。否则,你只是在做错误的决定。例如,假设你的 CEO 为你的产品创建了一个复杂的定价结构。你可以专注于使用您的界面和信息设计技巧使定价页面尽可能的清晰。但更难更重要的设计机会是与您的 CEO 一起重新定价你的产品,以便用户清楚,同时符合业务目标。 Focusing on the core business is what differentiates real product design from interface design or even user experience design. Fundamental product design is really hard and requires a lot of legwork, but this is what designers at the highest level do — and it’s why their work is better than yours. +关注核心业务是将是实际产品设计与界面设计甚至用户体验设计区分开来。基本的产品设计真的很难,需要大量的调研工作,但这就是最高级的设计师所做的-这就是为什么他们的工作比你的好。 ![](https://cdn-images-1.medium.com/max/600/1*czW-2nrN_3l50ZzgYQYqlw@2x.png) “The Disciplines of UX Design” by Dan Saffer and Thomas Gläser +Dan Saffer 和 ThomasGläser 的 「用户体验设计学科」 #### Grow your design team to include non-designers #### +#### 扩大您的设计团队,包括非设计师 #### Design is a hard job. You’ll need a wide range of skills (look at all of those circles in the[ diagram of UX Disciplines](https://www.fastcodesign.com/1671735/infographic-the-intricate-anatomy-of-ux-design) by Dan Saffer) and years of practice to truly master design. +设计是一项艰巨的任务,你将需要广泛的技能(请查看由 Dan Saffer(制作的)[UX 学科图](https://www.fastcodesign.com/1671735/infographic-the-intricate-anatomy-of-ux-design)中的所有圈子)和多年的实践才能真正掌握设计。 Maybe that’s why so many designers are offended when non-designers do design work or get called “designers” by Jared and me. You can act offended if you want, but the reality is that other people are making design decisions with or without you. Embrace them . They don’t make your job less valuable. They don’t make your job title less meaningful. +也许这就是为什么当不是设计师的人做设计工作或者被 Jared 和我称作「设计师」时,许多设计师感到生气。只要你愿意你可以表现的很生气,但事实是其他人不管有没有你都一直在做设计决策。拥抱他们吧,他们不会使你的工作变得不那么有价值,也不会使你的职位变得不那么有意义。 Having more people who *do design* is additive, not competitive. These designers make your team and your product stronger, because they’re contributing from their unique perspectives. Help them bolster their skills, and use their expertise to the advantage of your product and company. +对于更多的人来说**做设计**是附加的,并没有竞争力。这些设计是您的团队和您的产品更强大,因为他们从独特的角度做出贡献。帮助他们提高自己的技能,并利用自己的专长来优化您的产品和公司。 ### **Let’s make a better future together** ### +### **让我们一起创造更美好的未来吧** ### A few years ago, I met an executive from a Fortune 500 company. When I told her I’m a designer, her eyes lit up. “Oh, I love design!” she said. “My group is just down the hall from the design team. They do so much creative work in there.” +几年前,我遇到一家财富 500 强公司的 CEO。当我告诉她我是设计师时,她的眼睛亮了起来。「哦,我喜欢设计!」她说,「我的团队就在设计团队的大厅里,他们在那里做了很多创造性的工作。」 My heart sank. The design team was just down the hall from this executive, but they didn’t work together. Instead, they sat behind soundproof glass walls and did special “creative work” in isolation. +我的心冷了下来。设计团队只是在这个行政大厅,但是他们没有一起工作。相反,他们坐在隔音玻璃后面,孤立地做「创造性工作」。 This executive made decisions every day that affected her customers. She bears some of the blame for not reaching out to the designers “in there.” But the design team is primarily at fault — for missing an opportunity to reach out and work together on some of the business’s most important challenges. - +这个行政大厅每天作出影响他的顾客的决定。「在这里」却没有与设计师接触,她需要承担一部分责任。但主要错在设计团队,因为错过了接触和在一些业务中最重要的挑战上共同工作的机会。 Thanks to [Jared M. Spool](https://medium.com/@jmspool) for writing an excellent article, [John Zeratsky](https://medium.com/@jazer) and [Michael Margolis](https://medium.com/@mmargolis) for editing and suggestions, and [Alex Schleifer](https://medium.com/@alexoid) for the excellent *Wired* article. +感谢 [Jared M. Spool](https://medium.com/@jmspool) 拟写优秀的文章, [John Zeratsky](https://medium.com/@jazer) 和 [Michael Margolis](https://medium.com/@mmargolis) 参与编辑和建议, 以及 [Alex Schleifer](https://medium.com/@alexoid) 为优秀的**《连线》**文章(做的努力)。 + --- > [掘金翻译计划](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)、[React](https://github.com/xitu/gold-miner#react)、[前端](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)。 From 99b6cb77b0b76856ff03bd59e941d3fa8d7a452f Mon Sep 17 00:00:00 2001 From: ylq167 Date: Fri, 28 Apr 2017 15:51:28 +0800 Subject: [PATCH 282/638] =?UTF-8?q?=E5=B0=8F=E7=9A=84=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO/everyone-is-a-designer-get-over-it.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO/everyone-is-a-designer-get-over-it.md b/TODO/everyone-is-a-designer-get-over-it.md index 3d731931f0d..d2b20d831b0 100644 --- a/TODO/everyone-is-a-designer-get-over-it.md +++ b/TODO/everyone-is-a-designer-get-over-it.md @@ -11,7 +11,7 @@ Photo by Alice Achterhof [via Unsplash](https://unsplash.com/search/designer-pai 图片作者 Alice Achterhof [来自于 Unsplash](https://unsplash.com/search/designer-paint?photo=FwF_fKj5tBo) # Everyone is a designer. Get over it. # -# 人人都是设计师。忘了它吧。# +# 人人都是设计师。结束它吧。# Recently, [Jared Spool](https://www.uie.com/about/) caught my attention with [an article](https://articles.uie.com/signup/) about how Netflix’s performance engineers are *actually designers*. It’s a provocative idea, but it makes sense. His argument is that everyone in your organization (including performance engineers) designs the product, not just the people with “design” in their job titles. 最近,[Jared Spool](https://www.uie.com/about/) 通过一篇关于 Netflix 的性能工程师竟然是**设计师**的[文章](https://articles.uie.com/signup/)吸引了我的注意 。这是一个挑衅的想法,但也有一定的道理。他的论点是你团队中的每个人(包括性能工程师)都参与到产品设计,而不仅仅是职位为「设计」的人。 From 1acb4b09c28c14c639626fb139d2ef003890c8d4 Mon Sep 17 00:00:00 2001 From: Changkun Ou Date: Fri, 28 Apr 2017 14:06:00 +0200 Subject: [PATCH 283/638] =?UTF-8?q?(add):=20=E5=A2=9E=E5=8A=A0=E7=BF=BB?= =?UTF-8?q?=E8=AF=91=E5=88=9D=E7=A8=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO/how-to-pretend-youre-a-great-designer.md | 112 +++++++++--------- 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/TODO/how-to-pretend-youre-a-great-designer.md b/TODO/how-to-pretend-youre-a-great-designer.md index 251eb10769f..753892d29c9 100644 --- a/TODO/how-to-pretend-youre-a-great-designer.md +++ b/TODO/how-to-pretend-youre-a-great-designer.md @@ -1,138 +1,138 @@ > * 原文地址:[How to pretend you’re a great designer](https://thedesignteam.io/how-to-pretend-youre-a-great-designer-3625de90d79f) > * 原文作者:[Pablo Stanley](https://thedesignteam.io/@pablostanley?source=post_header_lockup) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -> * 译者: +> * 译者:[Changkun Ou](https://changkun.us/) > * 校对者: -# How to pretend you’re a great designer # -## Practical tips and principles to trick people into thinking you’re an industry thought leader. ## +# 设计师装逼指南 # + +## 假装成为行业思想领袖的经验和技巧 ## ![](https://cdn-images-1.medium.com/max/1000/1*8ted6GIeOq2hxnr9cxwtMw.gif) -Gabrielle will use delight until your pupils explode +> Gabrielle 将使用『喜悦』来解释全部的设计,直到你的瞳孔爆炸。 -### **Distract with delight** ### +### **使用『喜悦』来分散注意力** ### -Don’t know how to justify your excessive use of animations, clever copy, or generic *cute* illustrations? Just throw the word “delight” in the mix! Talk about how you understand the user’s psychology–how you’re creating an experience people will love. Who cares if your solution is not functional, expensive to build, or if there’s no data to back up your intuition. Remind everyone that you’re building a lasting and emotional connection with the user. +不知道如何解释你的过度使用动画、巧妙的抄袭,或者通用*可爱*的图案吗?只需要说它们通常伴随着『喜悦』就够了!谈谈你是如何理解用户的心理的 —— 如何打造一个人们会喜欢的体验。没有人关心你的方案是否功能健全、是否成本高昂,或者是否有数据支撑你的直觉。提醒所有人,你正在与用户建立持久的感情联系。 -***PRO TIP:*** *Show everyone a graph of Maslow’s Hierarchy of Needs and point out how the warm-and-fuzzy stuff is on the top.* +***装逼技巧:*** *向所有人展示 Maslow 的需求层次理论图,并指出最上层的『自我实现』是多么高层次的追求。* ![](https://cdn-images-1.medium.com/max/1000/1*U0hzgnxBdy6UWp-9mJsIOQ.gif) -Toby is a walking jargon-encyclopedia. - -### Confuse them using industry language ### +Toby 是一本活脱脱的百科全书. -Never mind that part of your job is making things easy to understand. Use phrases like *“A holistic approach”* or *“brand storytelling”* or other jargon that will keep people scratching their heads, afraid to ask what that even means. The more buzzwords you use, the less you have to explain your actual design thinking. +### 使用行业术语来迷惑他们 ### -If a stakeholder asks about your reasoning for a design, just say something like *“It’s social-minded, guided by our brand values, and built with personality to engage our users through empathy and emotional design”*, even when you’re just designing a coupon input box. +不要在意你的工作是否让事情变得容易理解。使用短语像『*整体分析*』或『*品牌故事*』亦或其他让人即摸不着头脑也不敢问是什么意思的行业术语。你使用的流行语越多,你就越不需要解释你的实际设计思路。 +如果一个利益相关人士询问你的设计理由,只要告诉他们诸如『这种设计以社会为导向,以品牌价值为指导,围绕其特性所建立,从而通过共鸣和情感设计来吸引用户』之类的话就可以了,哪怕你只是在设计一个优惠券的输入框。 ![](https://cdn-images-1.medium.com/max/1000/1*QsVGLGmSStDhlhNY_LKM9w.gif) -Fran advises to be, above all, consistent! +Fran 建议:首先,要保持一致! -### Use Consistency as your ONLY guiding principle ### +### 使用一致性作为你的唯一指导原则 ### -Ignore context––consistency is more important! Your users might have become familiar with iOS, Android, or web patterns but those patterns go against your style guide. You have to be consistent, even if it means the user needs to learn new ways of doing things––they have to check out your unique and sophisticated drop-down menu. +忽略情景 —— 一致性更重要!也许你的用户已经熟悉了 iOS、Android 或 Web 模式,但这些模式与你的设计风格是不一样的。你必须保持设计的一致,即便是需要增加用户学习成本 —— 但他们必须查看你独特且复杂的下拉菜单。 -If your PM asks for a design that is best suited for mobile, just say that you’re striving for consistency. And, can’t the developer just make everything responsive and stuff? Being consistent allows you to not think of different solutions for specific scenarios––you can just copy-paste from the kitchen sink and call it a day! +如果你的产品经理想要一个最适合移动端的设计,只需要说你正在努力让所有平台的设计保持一致。开发者难道不能让所有元素都做到响应式吗?保持一致性可以让你不用考虑特定场景下的不同解决方案 —— 你只用从所有的东西里复制粘贴就算是完成一天的工作了! ![](https://cdn-images-1.medium.com/max/1000/1*hm7Fr-D0-u4Rav6Og-hmLA.gif) -Paul understands aesthetics +Paul 理解美学。 -### **Make aesthetics a priority over functionality** ### +### **美学优于功能** ### -Forget about solving the problem with a flow that actually works. Obviously, you just need to copy all those flashy, unpractical effects you saw on Dribbble and apply them to your design. Who cares if research shows that sole icons are inefficient and hard to memorize or that stock photos rarely add value? Stakeholders will be so wowed by all the bouncy animations, Apple-like white space, and left-aligned Swiss types that no one will question if it even works. +忘记用实际有用的工作流程来解决问题。显然,你只需要复制所有在 Dribbble 上看到那些华而不实的效果,并将其运用到你的设计之中。没有人关心调查显示的单个图标效率很低且很难被记住,以及库存的图片根本没有价值这些结论。所有的利益相关者都会因为那些具有弹性动画、像苹果一样的大量留白,和左对齐的瑞士主义设计风格一样感到震惊,没有人会怀疑它能否奏效。 ![](https://cdn-images-1.medium.com/max/1000/1*BVWmKNOrZ5uVQs9YU2Hw-Q.gif) -Gabrielle uses her magic to turn other products’ solutions into her own! +Gabrielle 用她的魔力将其他产品的解决方案变成了自己的! -### **Learn (copy) from the best** ### +### **学习(照搬)最佳实践** ### -Don’t know how to solve a problem? Just replicate the solution from Amazon, Apple, or Facebook and apply your own style on top of it. When someone challenges you, just say *“that’s how Amazon does it”*. That will teach them to keep questions to themselves next time! If it worked for Amazon, it must work for you, right? Who cares if you’re not even a marketplace or e-commerce product. +不知道如何解决问题?只需要复制亚马逊、苹果或者 Facebook 的解决方案,并运用自己的风格即可。当有人质疑你时,只要说 *『亚马逊就是这么干的』*。这将教会他们将问题保留到下一次再提问!如果它适用于亚马逊,它肯定适合你,对吧?因为根本没有人在乎你搞的是不是一个电商产品。 ![](https://cdn-images-1.medium.com/max/1000/1*_rH6r2v-eKIY9doQOz86jw.gif) -Claude knows how to make a pie chart! +Claude 知道如何制作一个饼状图。 -### **Use research bias** ### +### **使用调查偏差** ### -Do you have to justify that design that you spent time on all week? Just create a beautiful graph with cherry-picked data from a survey or test you conducted. The bigger the numbers, the more you’ll impress people with your interpretation! +你是否必须为你花了一整个星期时间搞出来的设计辩护?只需用你进行过的调查或测试中精心挑选的数据创建一个花哨的图表就可以了。数字越大,你的解释就会让人们的印象越深刻! -If anyone asks to see your methods or sources or wants to see the raw data, just tell them you should take that conversation offline and avoid them for the rest of the month. +假如有人要求查看你的方法或来源或想要查看原始数据,只需告诉他们你们的对话应该结束了,然后在这个月剩下的时间里躲避他们就好。 -***PRO TIP:*** *If you have the skills, make a spurious correlation slide that will have everyone nodding their heads. E.g. “The bounce rate of our landing page correlates with the downloads of Pokemon Go, therefore, we should add Augmented Reality to our page”.* +***装逼技巧:*** *如果你有能力,做一个假的幻灯片,让大家接受你的结论。 比如,『我们的登录页的跳出率与 Pokemon Go 的下载量相关,因此我们应该将增强现实添加到我们的页面』。* ![](https://cdn-images-1.medium.com/max/1000/1*mgeKX-DlW-obHhFUGBB6xA.gif) -Fran looks at her work in deep silence. +Fran 在沉默中看着她的工作。 -### **Make people think you’re busy** ### +### **让人们觉得你很忙** ### -There’s nothing better than having a wall full of other people’s work to impress passers-by. Print screenshots of other products and call it “competitive analysis”. Add all your Pinterest pins from other talented designers and call it “inspiration”. Make collages of all the terrible iterations you explored and call it “ideation”. Go for quantity, not quality. It doesn’t matter if what’s pinned to the wall makes any sense or if there’s any actual substance. The important thing is to make people believe that you have a creative and busy mind. +没有什么比满墙贴满他人的作品更能给路人留下深刻印象的了。打印其他产品的截屏,这叫『竞品分析』;把所有的 Pinterest 上有才华的设计都放上去,这叫『灵感来源』;把所有你探索过的糟糕的迭代过程做成拼图放在一起,这叫『想法形成』。始终追求数量,而不是质量。不管是有意义还是没意义的,钉上在墙后都是有意义的。重要的是要让人们相信你有一个有创造力且忙碌的头脑。 -***PRO TIP:*** *Stare at the wall for hours in silence. Your coworkers will think you’re really contemplative.*[*Draw eyes on your eyelids*](https://www.youtube.com/watch?v=U6qBnykH0DU) *and you can sleep while doing this.* +***装逼技巧:*** *默默地盯着墙看几个小时,你的同事会认为你在沉思。如果你**[在你的眼皮上画上眼睛](https://www.youtube.com/watch?v=U6qBnykH0DU),还能打会盹儿。* ![](https://cdn-images-1.medium.com/max/1000/1*gB-SyYrl3WXBYDW2Up7lWQ.gif) -Petunia is becoming a great designer! +Petunia 马上就要成为一个伟大的设计师了! -### Use ambiguous visuals ### +### **使用模棱两可的视觉表现** ### -Everybody can draw a Venn Diagram, but can they do it with perfect equilateral triangles? +大家都会画韦恩图,但他们能用完美的等边三角形来做吗? -Impress your coworkers with a double ▲▲ or a triple▲▲▲ triangle diagram. It won’t only make everyone nod at your confusing concepts, but it will establish you as a creative mind that uses simplicity to explain complex ideas. +你可以使用双重 ▲▲ 甚至三重 ▲▲▲ 三角形图来打动你的同事。这不仅会让每个人都同意你偷换的概念,还会让他们觉得你你具有创造性思维,并用简单的方式解释复杂的想法。 -***PRO TIP:*** *Always put the word “VALUE” in the overlapping area of the diagram!* +***装逼技巧:*** *总是将『价值』写在图标相交的区域里!* ![](https://cdn-images-1.medium.com/max/1000/1*VImvvTlomv4aX8E_UbUlUQ.gif) -Paul knows how to create a tailor-made product. +Paul 知道如何创建一个量身定做的产品。 -### **Misinterpret Human-Centered Design** ### +### **扭曲用户中心设计** ### -You’re not a modern designer if you don’t use Human-Centered Design (HCD for pros). And when I say “use”, I actually mean “talk about it”. Lecture everyone about how they need to understand the needs and abilities of the users. How building empathy is crucial to creating meaningful products. Who cares if other things have been successfully designed in the past without this approach? Don’t bother if this process might not be the best for you. Never mind if tailoring a product to a certain demographic can alienate others. It doesn’t matter if sometimes it’s in the user’s best interest if you ignore their suggestions. Better to impress everyone with how deeply you *care* about the user. +如果你不用『用户中心设计』(HCD),那么你不够现代。当我们谈论『用』时,我们实际在谈论『讨论它』而已。让每个人都知道他们需要如何理解用户的需求和能力。如何建立共鸣对创造有意义的产品至关重要。如果没有用户中心设计,谁还会在意其他事情是否已经成功地设计好了呢?不用在意这个方法是否适合你,更不用在意如果把产品定制为某种特定的人群是否会疏远他人。如果你忽略了他们的建议,这并不重要。用你是多么地『*关心*』用户来打动大家就可以了。 ![](https://cdn-images-1.medium.com/max/1000/1*J0AFQkt36gUyNqjaFm3lIw.gif) -Paul sure understands his main user. +Paul 确实了解他的主要用户。 -### **Stay focused on your market, which is you** ### +### **专注你的市场** ### -Oh, crap. They actually believed you on the whole human-centered thing and now they want an accessible design? Don’t worry, just point out that making a site accessible can take a long time and money. Say that your target market has state-of-the-art computers and internet connection, and they’re all healthy millennials that never even see the doctor. How do you know? You’re one of them! You’re exactly like your users! +好吧,他们信任你的那套『用户中心设计』了,现在他们想要一个无障碍设计怎么办?不要担心,你只需要指出,对网站进行无障碍需要花很长时间和很多钱。然后假设你的目标市场有着最先进的计算机和互联网连接,而且用户都是健康、甚至都没有见过医生的 00 后就可以了。你怎么知道的?因为你是他们中的一员!你自己就是用户! -And, let’s be honest, accessible design could keep you away from all those marvelous scroll-jacking animations you added to your landing page. +而且,老实说,无障碍设计可能让你永远没法在页面上添加的那些炫酷动画了。 -> Text labels? Ugly! -> High contrast combinations? Awful! -> Clear navigation? Despicable! +> 文本标签?太丑了! +> 高对比度组合?很糟糕! +> 清晰的导航?毫无意义! -Don’t concern yourself with reaching a bigger audience by making your design accessible, or with how it actually improves usability or makes your site SEO friendly. You need those parallax animations and that subtle low-contrast color palette that works only on a thunderbolt display. +不用担心那些为了能获得更多受众而进行的无障碍设计、可用性提升,或者是让网站变得 SEO 友好。你只需要使用视差滚动效果和一台具有精细的低对比度调色板的雷电接口的显示器就足够了。 -### On a serious note ### +### 严肃的说 ### -While all of these “tips” are meant to be tongue-in-cheek, they illustrate sins I myself have committed in the past. And I could say that it’s just a byproduct of how we’re all figuring stuff out as we go. That we’re experimenting with different things, seeing what works and what doesn’t–adapting to the ever-changing user’s needs and technology. And all would be understandable on the surface, but deep down, I have to admit… I sometimes don’t know if what I’m doing is right. +而上面所有这些『技巧』都是不靠谱的,它们其实展示了我自己曾经犯过的错误。可以这么说,这些只是我们所做的一切事情的副作用。我们正在不断尝试不同的事物,看看什么是有效的、什么是不适应不断变化的用户的需求和技术。从表面上看,一切都是可以理解的,但我必须承认,我有时不知道我所做的事情是否正确。 -Do you ever feel like that? Fear of the moment when everyone realizes you’re some kind of fraud? That you’re in the process of faking-it-till-you-make-it? I recently learned they call it the “Impostor Syndrome”. I had never felt this as strongly as when I moved to San Francisco. As soon as I started working at a startup, I realized there was a lot I needed to learn. I found myself feeling insecure and lost–utilizing my charm to get other people’s approval. I didn’t think my design work was enough. +不知道你是否有这样的感觉:当所有人都意识到你是一个骗子的时候,你是否会感到害怕吗?你是否曾今假装自己能做到?我最近听说这叫『骗子综合症』。我从来没有像搬到旧金山之后感受如此强烈。当我开始在一家创业公司工作时,我发现我需要学习很多东西。我感到不安,且失去了利用我的个人魅力来获得别人认可的能力。所以我觉得我的设计工作还做得不够好。 -But anyway, I believe it’s getting better. Or at least I’ve gotten better at pretending. +无论如何,我相信情况正在好转。或者至少我已经变得更加善于伪装了。 -**The Design Team** +**设计团队** -New comic every other Taco Tuesday at[thedesignteam.io](http://thedesignteam.io) -[Subscribe](http://eepurl.com/cbWwtz) to get my spam.I’m [Pablo Stanley](https://twitter.com/pablostanley) . Among many things, I teach [courses on design](https://www.youtube.com/c/sketchtogethertv) , and I work on a healthcare startup called [Carbon Health](https://carbonhealth.com/). +- [thedesignteam.io](http://thedesignteam.io) 上的漫画每周二更新。 +- 我是 [Pablo Stanley](https://twitter.com/pablostanley) 。点击 [订阅](http://eepurl.com/cbWwtz) 可以收到我的『垃圾邮件』。我教授一门 [设计课程](https://www.youtube.com/c/sketchtogethertv),同时我还在一家名为 [Carbon Health](https://carbonhealth.com/) 的医疗保健创业公司工作。 -Thanks to [Courtney M. Sawyer](https://medium.com/@courtneymsawyer) , [Michael Lorton](https://medium.com/@michaellorton) , [Edgar chaparro](https://medium.com/@Echaparro), and [Frances Tung](https://medium.com/@francestung) for all their support. +- 感谢 [Courtney M. Sawyer](https://medium.com/@courtneymsawyer) , [Michael Lorton](https://medium.com/@michaellorton) , [Edgar chaparro](https://medium.com/@Echaparro), 和 [Frances Tung](https://medium.com/@francestung) 的全部支持。 > [Read Previous: The Design Process](https://thedesignteam.io/the-design-process-67df3e8ec68f#.lv47slyvv) > [Another cool one: From a Product Perspective](https://thedesignteam.io/from-a-product-perspective-2f5185a43827) > [The First One: The Onboard-a-Buddy](https://medium.com/the-design-team/the-onboard-a-buddy-71169e460f04#.iru2t3tub) -Thanks to Edgar Chaparro, Michael Lorton, and Courtney Sawyer. +- 感谢 Edgar Chaparro, Michael Lorton 以及 Courtney Sawyer. --- From 38a0af3bca49e37e1afb8cacf7d8920d2ff24443 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ARX-8=5C=E6=AC=A7=E9=98=B3=E5=A4=8F=E6=98=B1?= Date: Sat, 29 Apr 2017 00:04:59 +0800 Subject: [PATCH 284/638] =?UTF-8?q?2017.4.29=20=E6=A0=B9=E6=8D=AE=E6=A0=A1?= =?UTF-8?q?=E5=AF=B9=E6=84=8F=E8=A7=81=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...le-developer-android-rxjava2-hell-part5.md | 170 ++++++++++-------- 1 file changed, 97 insertions(+), 73 deletions(-) diff --git a/TODO/dialogue-rx-observable-developer-android-rxjava2-hell-part5.md b/TODO/dialogue-rx-observable-developer-android-rxjava2-hell-part5.md index 196ea5c7679..1c310ee6d4d 100644 --- a/TODO/dialogue-rx-observable-developer-android-rxjava2-hell-part5.md +++ b/TODO/dialogue-rx-observable-developer-android-rxjava2-hell-part5.md @@ -6,24 +6,24 @@ ## Dialogue between Rx Observable and a Developer (Me) [ Android RxJava2 ] ( What the hell is this ) Part5 ## -## 我与被观察君的一段对话 [ Android RxJava2 ] ( 这是什么鬼? ) Part5 ## +## 开发者( 也就是我 )与Rx Observable 类 [ Android RxJava2 ] ( 这到底是什么? ) 第五部分 ## WOW, we got one more day so its time to make this day awesome by learning something new 🙂. -哇哦, 又是新的一天啦。是时候学习新姿势,让这一天碉堡了。 +又是新的一天,是时候学点新东来西来让今天变得酷炫了🙂。 Hello guys, hope you are doing good. This is our fifth post in series of RxJava2 Android [ [part1](http://www.uwanttolearn.com/android/reactive-programming-android-rxjava2-hell-part1/), [part2](http://www.uwanttolearn.com/android/pull-vs-push-imperative-vs-reactive-reactive-programming-android-rxjava2-hell-part2/), [part3](http://www.uwanttolearn.com/android/functional-interfaces-functional-programming-and-lambda-expressions-reactive-programming-android-rxjava2-what-the-hell-is-this-part3/), [part4](http://www.uwanttolearn.com/android/war-learning-curve-rx-java-2-java-8-stream-android-rxjava2-hell-part4/) ]. In this part we are going to work with Rx Java Android. Our prerequisites are done. So we can start now. -哥几个好啊,希望大家最近过的不错。这是我们一系列有关 RxJava2 Android [ [part1](http://www.uwanttolearn.com/android/reactive-programming-android-rxjava2-hell-part1/), [part2](http://www.uwanttolearn.com/android/pull-vs-push-imperative-vs-reactive-reactive-programming-android-rxjava2-hell-part2/), [part3](http://www.uwanttolearn.com/android/functional-interfaces-functional-programming-and-lambda-expressions-reactive-programming-android-rxjava2-what-the-hell-is-this-part3/), [part4](http://www.uwanttolearn.com/android/war-learning-curve-rx-java-2-java-8-stream-android-rxjava2-hell-part4/) ] 的第五篇文章。在这篇文章里,我们会与 Rx Java Android 一起研究学习。现在万事俱备,我们一起来吹个东风。 +大家好,希望你们都过的不错。这是我们 RxJava2 Android 系列的第五篇文章 [ [part1](http://www.uwanttolearn.com/android/reactive-programming-android-rxjava2-hell-part1/), [part2](http://www.uwanttolearn.com/android/pull-vs-push-imperative-vs-reactive-reactive-programming-android-rxjava2-hell-part2/), [part3](http://www.uwanttolearn.com/android/functional-interfaces-functional-programming-and-lambda-expressions-reactive-programming-android-rxjava2-what-the-hell-is-this-part3/), [part4](http://www.uwanttolearn.com/android/war-learning-curve-rx-java-2-java-8-stream-android-rxjava2-hell-part4/) ] 。在这篇文章中,我们会继续研究 Rx Java Android 。 **Motivation:** -*目标:** +**动机**: Motivation is same which I share with you in [part1](http://www.uwanttolearn.com/android/reactive-programming-android-rxjava2-hell-part1/). This time we will see, a lot of things in action which we already learned in last four posts. -目标和我在 [part1](http://www.uwanttolearn.com/android/reactive-programming-android-rxjava2-hell-part1/)中分享给大家的一样。现在我们来之前4篇学的东西融会贯通起来。 +动机和我在第一部分 [part1](http://www.uwanttolearn.com/android/reactive-programming-android-rxjava2-hell-part1/) 中分享给大家的一样。现在我们来把之前 4 篇学到的东西融会贯通起来。 **Introduction:** @@ -31,71 +31,71 @@ Motivation is same which I share with you in [part1](http://www.uwanttolearn.com When I am learning Rx Java Android. One day I met with a Rx Java Observable. So I asked a lot of questions and good news is Observable is really good and down to earth which amazed me. I have a opinion this Rx Java is really bad and full of pride. He/She don’t want to make friendship with lot of developers instead that play with developers and bluff them but after I start my dialogue with him/her I am amazed my opinion is wrong. -当我在学习Rx java Android的时候,有一天我碰到了一个Rx Java的被观察君。所以我问了好多问题,好消息是被观察君人很好很厚道,令我惊叹不已。我一直以为 Rx Java 是个大坑逼。他/她不想和开发者做朋友,总给他们穿小鞋。 -但是在和他/她谈话以后,我很惊讶我的观点是错的。 +当我在学习 Rx java Android 的某一天,我有幸与一位 Rx Java 的 Observable 类进行了亲切友好的交谈。好消息是 Observable 类很厚道,令我惊叹不已。我一直以为 Rx Java 是个大坑逼。他/她不想和开发者做朋友,总给他们穿小鞋。 +但是在和 Observable 类谈话以后,我惊喜的发现我的观点是错的。 Me: Hello Observable. How are you? -我:你好被观察君,吃了嘛您? +我:你好,Observable 类,吃了嘛您? Observable: Hi Hafiz Waleed Hussain, I am good. Thank you. -你好 Hafiz Waleed Hussain ,吃过啦。 +Observable 类:你好 Hafiz Waleed Hussain ,我吃过啦。 Me: Why your learning curve is really tough? Why you are not easy for developers? You don’t want to make friendship with Developers. -为啥你的学习曲线这么陡峭?为啥你故意刁难开发者?你这是拒人于千里之外啊。 +我:为啥你的学习曲线这么陡峭?为啥你故意刁难开发者?你这么搞要没朋友了。 Observable: Haha. Truly saying. I really want to make lot of friends instead I have some very good friends. Which discuss about me on different forums and they are talking about me and my powers. These guys are really good in lot of things, they are spending a lot of hours with me. So for a good friend ship you need to give your time with sincerity. But there is one issue, some developers wants to make friendship with me but they are not sincere. They start working on me but after some minutes they open social websites and forgot me about hours. So how you can expect from me, I will be a good friend of a developer who is not sincere with me. -被观察者君:木哈哈,你说的是。我真想交很多朋友,不过我现在也有一些好哥们儿。他们在不同的论坛上谈论我,介绍我和我的洪荒之力。而且这些家伙疹的很棒,他们花了很久的时间和我呆在一起。只有精诚所至,才会金石为开。但问题是,很多想撩我的人只走肾不走心。他们关注我了一小会就去刷微博,把我给忘了。想要马儿跑,又想马儿不吃草,我也很为难啊! +Observable 类:哈哈,你说的是。我真想交很多朋友,不过我现在也有一些好哥们儿。他们在不同的论坛上讨论我,介绍我和我的能力。而且这些家伙真的很棒,他们花了很久的时间和我呆在一起。只有精诚所至,才会金石为开。但问题是,很多想撩我的人只走肾不走心。他们关注我了一小会就去刷推特,把我给忘了。所以说,对我不真诚的人又如何指望我和他们交朋友呢? Me: Okay If I want to make a friendship with you. What I will do? -我:好吧,我想和你捡肥皂,我该怎么做? +我:好吧,如果想和你交朋友的话,我该怎么做? Observable: Do a proper focus on me. Give me proper time then you will see how frankly I am. -被观察君:如果你愿意一层一层一层一层的剥开我的心,你才会看到我的全心全意。 +Observable 类:把注意力集中在我身上,并且坚持足够长的时间,然后你就知道我有多真诚了。 Me: Hmm. Honestly I am not good in focus but I am good in ignoring. Can I use my ignoring power. -我:嗯,实话实说我不是一个专心的人,但是我擅长走神。这样可以嘛? +我:嗯,实话实说我不擅长集中精神,但是我擅长无视周围。这样可以嘛? Observable: Yes, if you ignore everything except me when you are working on me. I will be a your good friend. -被观察君:当然,如果你和我在一起的时候可以两耳不闻窗外事,伦家就是你的人了。 +Observable 类:当然,只要你和我在一起的时候可以心无旁骛,我会是你的好朋友的。 Me: Wow. I have a feeling then I can make you my friend. -我:哇哦,我有种预感:你会是我的好基友的。 +我:哇哦,我有种预感,我会和你交上朋友的。 Observable: Yes any body can make me his best friend. -被观察军:当然,任何人都可以把我当好基友。 +Observable 类:当然,任何人都可以把我当好朋友。 Me: Now I have some questions. Can I ask? -我:现在我有些问题,我可以问了嘛? +我:现在我有些问题,可以问了嘛? Observable: Yes you can ask thousands of questions. I will give you answer but one important thing I need your time with sincerity. -被观察君:当然,你可以问成千上万个问题。我会给你答案,但是重要的是我需要你花时间去思考和吸收。 +Observable 类:当然,你可以问成千上万个问题。我会给你答案,但是重要的是需要你自己花时间去思考和吸收。 Me: Sure. If I have a some data and I want to convert that into Observable. How I can achieve that in Rx Java 2 Android. -我:当然。如果我想把数据转化为 Observable 类,在 Rx Java 2 Android 里怎么实现? +我:我会的。如果我想把数据转化为 Observable 对象,在 Rx Java 2 Android 里怎么实现? Observable: This question which you asked me has a very long answer. If you go inside of me (Rx Java 2 Observable class). You will know I have total **12904** lines of code. -被观察君:这个问题的答案很长很长。如果你来看我(Rx Java 2 Observable 中的 Observable 类)的源码,你就会发现我一共有12904行代码。 +Observable 类:这个问题的答案很长很长。如果你来看我(Rx Java 2 Observable 类)的源码,你就会发现我一共有12904行代码。**(校对 wbinarytree 注:在 RxJava 2.0.9 版本。Observable 类已经成功增肥到 13728 行。)** [![](http://www.uwanttolearn.com/wp-content/uploads/2017/03/Screen-Shot-2017-03-18-at-8.54.00-AM-1024x527.png)](http://www.uwanttolearn.com/wp-content/uploads/2017/03/Screen-Shot-2017-03-18-at-8.54.00-AM.png) Every method will return you Observable. Yes I have a lot of friends in my community which I use to make my self according to Developer requirement like map, filter and more but I am here going to share with you some methods which will help you to make any thing as Observable. Sorry because I have a feeling answer will be long but that will not be boring. I will not only show you methods to create Observable instead I will share with you how you refactor your current data objects to Observable with suitable method. -我身边也有好几个臭皮匠,可以根据开发者的需求返回 Observable 类,比如 map ,filter。不过现在我会告诉你几个可以帮助你把任何东西转化为 Observable 类的方法。抱歉我决定回答会很长,但是也不会很无聊。我不仅仅会演示这些方法如何创建 Observable 类,同时我也会和你分享对你手头边代码进行重构的正确姿势。 +我的团队里也有好几个朋友,可以根据开发者的需求返回 Observable 对象,比如 map ,filter。不过现在我会告诉你几个可以帮助你把任何东西转化为 Observable 类的方法。抱歉我的回答可能会很长,但是也不会很无聊。我不仅仅会演示这些方法如何创建 Observable 类,同时也会和你像你展示如何对手头边代码进行重构。 1. just(): @@ -103,36 +103,36 @@ Every method will return you Observable. Yes I have a lot of friends in my commu By using this method you can convert any object(s) into Observable that emit that object(s). -通过这个方法,你可以把任意(多个)类转化成泛型是该类的 Observable 类。 +通过这个方法,你可以把任意(多个)对象转化成以此对象为泛型的 Observable 对象( Observable )。 ``` - String data = "Hello World"; +String data= "Hello World"; Observable.just(data).subscribe(s -> System.out.println(s)); - Output: +Output: Hello World ``` If you have more then one data objects you can use same API like shown below. -如果有不止一个的数据,你可以像下面那样调用 just 方法 : +如果你的数据不止一个,可以像下面那样调用 just 方法 : ``` - String data = "Hello World"; - Integer i = 4500; - Boolean b = true; +String data= "Hello World"; +Integer i= 4500; +Boolean b= true; Observable.just(data,i,b).subscribe(s -> System.out.println(s)); - Output: +Output: Hello World 4500 true ``` Maximum you can use 10 data objects in this API. -此 API 最大可接收 10 个数据做参数。 +此 API 最多可接收 10 个数据做参数。 ``` Observable.just(1,2,3,4,5,6,7,8,9,10).subscribe(s -> System.out.print(s+" ")); - Output: +Output: 1 2 3 4 5 6 7 8 9 10 ``` @@ -145,14 +145,14 @@ Example in code: ( Not good one but may be you will get some direction, how to u ``` public static void main(String[] args) { - String username = "username"; - String password = "password"; +String username= "username"; +String password= "password"; System.out.println(validate(username, password)); } private static boolean validate(String username, String password) { - boolean isUsernameValid = username!=null && !username.isEmpty() && username.length() > 3; - boolean isPassword = password!=null && !password.isEmpty() && password.length() > 3; +boolean isUsernameValid= username!=null && !username.isEmpty() && username.length() > 3; +boolean isPassword= password!=null && !password.isEmpty() && password.length() > 3; return isUsernameValid && isPassword; } @@ -164,35 +164,39 @@ Using Observable: 使用 Observable 类进行重构: ``` -private static boolean isValid = true; +private static boolean isValid= true; private static boolean validate(String username, String password) { Observable.just(username, password).subscribe(s -> { - if (!(s != null && !s.isEmpty() && s.length() > 3)) +if (!(s != null && !s.isEmpty() && s.length() > 3)) throw new RuntimeException(); - }, throwable -> isValid = false); +}, throwable -> isValid= false); return isValid; } ``` -2. from… : +2. from…: -2. from… : + +2. from…: I have a lot more API to convert your complex data structure into Observable which starting keyword is from as shown below. -我有一大堆的 API 可以把复杂的数据结构转化为 Observable 类,比如下面那些以关键字 from 开头的方法: +我有一大堆的 API 可以把复杂的数据结构转化为 Observable 对象,比如下面那些以关键字 from 开头的方法: [![](http://www.uwanttolearn.com/wp-content/uploads/2017/03/Screen-Shot-2017-03-18-at-10.02.40-AM-1024x187.png)](http://www.uwanttolearn.com/wp-content/uploads/2017/03/Screen-Shot-2017-03-18-at-10.02.40-AM.png) I think API name are really meaning full so no need for more explanation. Yes I will give some examples so you are comfortable when you are using in code. -我想这些 API 的从名字就可以看懂它们的意思,所以也不需要更多解释了。不过我会给你一些例子,这样你可以在自己的代码里用的更舒服。 +我想这些 API 从名字就可以看懂它们的意思,所以也不需要更多解释了。不过我会给你一些例子,这样你可以在自己的代码里用的更舒服。 + +**(校对 wbinarytree 注: +虽然 fromCallable, fromPublisher, fromFuture 也是 from 开头的方法。但是他们互相之间区别很大。尤其是 fromCallable 和 fromPublisher。)** ``` public static void main(String[] args) { - List tasks = Arrays.asList(new Tasks(1,"description"), +List tasks= Arrays.asList(new Tasks(1,"description"), new Tasks(2,"description"),new Tasks(4,"description"), new Tasks(3,"description"),new Tasks(5,"description")); Observable.fromIterable(tasks) @@ -201,9 +205,9 @@ public static void main(String[] args) { private static class Tasks { int id;String description; - public Tasks(int id, String description) {this.id = id;this.description = description;} +public Tasks(int id, String description) {this.id= id;this.description = description;} @Override - public String toString() {return "Tasks{" + "id=" + id + ", description='" + description + '\'' + '}';} +public String toString() {return "Tasks{" + "id=" + id + ", description='" + description + '\'' + '}';} } } ``` @@ -215,7 +219,7 @@ From array: ``` public static void main(String[] args) { - Integer[] values = {1,2,3,4,5}; +Integer[] values= {1,2,3,4,5}; Observable.fromArray(values) .subscribe(v-> System.out.print(v+" ")); } @@ -226,16 +230,21 @@ Here two examples are enough. You can try others on your own. 两个例子就够啦,回头你可以亲自试试其他的。 +3. create(): + + 3. create(): You can define any thing you want as an Observable. This API will give you a lot of power but in my opinion before going to use this API try to search some other solution because I have a feeling 99% times you can get solution from my other API’s. If you are not able to get any solution of something then you can use. -你可以把任何东西的定义为 Observable 。这个 API 过于勥,所以在我看来使用这个API之前,应该先找找有没有其他的解决方式。大约99%的情况下,你可以用其他的 API 来解决问题。如果实在找不到解决方式,那么就用它吧。 +你可以把任何东西强行转为 Observable 对象。这个 API 过于强大,所以个人建议使用这个API之前,应该先找找有没有其他的解决方式。大约99%的情况下,你可以用其他的 API 来解决问题。但如果实在找不到,那么就用它也可以。 + +**(校对 wbinarytree 注:这里可能作者对 RxJava 2 的 create 还停留在 RxJava 1 的阶段。 RxJava 1.x 确实不推荐 create 方法。而 RxJava 2 的 create 方法是推荐方法。并不是 99% 的情况都可以被取代。 RxJava 1.x 的 create 方法现已经成为 RxJava 2.x 的 unsafeCreate ,RxJava 1.2.9 版本也加入了新的安全的 create 重载方法。)** ``` public static void main(String[] args) { - final int a = 3, b = 5, c = 9; - Observable me = Observable.create(new ObservableOnSubscribe() { +final int a= 3, b = 5, c = 9; +Observable me= Observable.create(new ObservableOnSubscribe() { @Override public void subscribe(ObservableEmitter observableEmitter) throws Exception { observableEmitter.onNext(a); @@ -250,6 +259,10 @@ You can define any thing you want as an Observable. This API will give you a lot ``` 4. range(): + +4. range(): + + That is just like a for loop as shown below in code. 这就像是一个 for 循环,就像下面的代码显示的那样。 @@ -259,7 +272,7 @@ That is just like a for loop as shown below in code. Observable.range(1,10) .subscribe(i-> System.out.print(i+" ")); } - Output: +Output: 1 2 3 4 5 6 7 8 9 10 ``` @@ -270,24 +283,29 @@ One more real example: ``` public static void main(String[] args) { - List names = Arrays.asList("Hafiz", "Waleed", "Hussain", "Steve"); - for (int i = 0; i < names.size(); i++) { - if(i%2 == 0)continue; +List names= Arrays.asList("Hafiz", "Waleed", "Hussain", "Steve"); +for (int i= 0; i < names.size(); i++) { +if(i%2== 0)continue; System.out.println(names.get(i)); } Observable.range(0, names.size()) - .filter(index->index%2==1) +.filter(index->index%2==1) .subscribe(index -> System.out.println(names.get(index))); } ``` +5. interval(): + + 5. interval(): This one is awesome. I am showing you one example in which you can compare two approaches. For first one I used a Java thread and for a second one I used my own interval() API and both have same result. -这个 API 碉堡了。我用两种方法实现一种需求,你可以比较一下。第一种我用 Java 的线程来实现,另一种我用 interval() API ,两种方法会得到同一个结果。 +这个 API 碉堡了。我用两种方法实现同一种需求,你可以比较一下。第一种我用 Java 的线程来实现,另一种我用 interval() API ,两种方法会得到同一个结果。 + +**(校对 wbinarytree 注:interval() 会默认在 Scheduler.compute 进行操作。)** ``` public static void main(String[] args) { @@ -309,11 +327,14 @@ public static void greeting(){ } ``` +6. timer(): + + 6. timer(): One more good API. In program if I want some thing will called after one second I can use timer Observable as shown below. -又是一个好的 API。在程序中如果我想一秒钟后调用什么方法,可以用 timer Observable ,就像下面展示的那样: +又是一个好的 API。在程序中如果我想一秒钟后调用什么方法,可以用 timer ,就像下面展示的那样: ``` public static void main(String[] args) throws InterruptedException { @@ -332,7 +353,7 @@ public static void greeting(){ This is useful specially in mocking. This create Observable that emit nothing and complete. I am showing you one example in which if tests are running then send me mock data else the real one. -这个 API 很有用,尤其是在有假数据的时候。这个 API 创建了一个什么都不包含,只有 complete 方法的 Observable 。我会给你一个例子,如果在测试运行时发送给我假数据,在生产环境下就调用真的数据。 +这个 API 很有用,尤其是在有假数据的时候。这个 API 创建了一个什么都不包含,只有 complete 方法的 Observable 类 。比如这个例子,如果在测试运行时发送给我假数据,在生产环境下就调用真的数据。 ``` public static void main(String[] args) throws InterruptedException { @@ -340,7 +361,7 @@ public static void main(String[] args) throws InterruptedException { } private static Observable hey(boolean isMock) { - return isMock ? Observable.empty() : Observable.just(1, 2, 3, 4); +return isMock ? Observable.empty(): Observable.just(1, 2, 3, 4); } ``` @@ -352,11 +373,11 @@ This is very use full in many cases. I am going to explain this one by using one ``` public static void main(String[] args) throws InterruptedException { - Employee employee = new Employee(); - employee.name = "Hafiz"; - employee.age = 27; - Observable observable = employee.getObservable(); - employee.age = 28; +Employee employee= new Employee(); +employee.name= "Hafiz"; +employee.age= 27; +Observable observable= employee.getObservable(); +employee.age= 28; observable.subscribe(s-> System.out.println(s)); } @@ -373,7 +394,7 @@ private static class Employee{ What will be the output of the above code. If your answer is age should be 28 then you are wrong. Basically all creation methods of Observable will take the value which is available at the time of creation. Like if I do output I will get 27 because I create an Observable at that time when I have age 27 and later I change to 28 but observable already created. So what will be the solution? Yes you can use defer API. That is really helpful. When you use defer basically what happen Observable only created when you will subscribe so its mean by using this I will get my expected result. -上面的代码会输出什么呢?如果你的答案是 age = 28 那就大错特错了。基本上所有创建 Observable 的方法在创建时就记录了可用的值。就像刚才的数据实际上输出的是 age = 27 , 因为在我创建 Observable 的时候 age 值是 27 ,当我把 age 的值变成 28 的时候 Observable 类已经创建过了。所以怎么解决这个问题呢?是的,这个时候就轮到 defer API 出厂了。太有用了!当你使用 defer 的时候,只有注册(subscribe)的时候才创建 Observable 类。用这个 API ,我就可以获得想要的值。 +上面的代码会输出什么呢?如果你的答案是 age= 28 那就大错特错了。基本上所有创建 Observable 的方法在创建时就记录了可用的值。就像刚才的数据实际上输出的是 age = 27 , 因为在我创建 Observable 的时候 age 值是 27 ,当我把 age 的值变成 28 的时候 Observable 类已经创建过了。所以怎么解决这个问题呢?是的,这个时候就轮到 defer 这个 API 出场了。太有用了!当你使用 defer 以后,只有注册(subscribe)的时候才创建 Observable 类。用这个 API ,我就可以获得想要的值。 ``` Observable getObservable(){ @@ -386,25 +407,27 @@ Now this time my age on output is 28. 这样我们的 age 的输出值就是 28 了。 +**(校对 wbinarytree 注:Observable 的创建方法中,并不是像原文中写到的,“基本上所有创建 Observable 的方法在创建时就记录了可用的值”。而是只有 just, from 方法。 create , fromCallable 等等方法都是在 subscribe 后才会调用。文中的例子可以使用 fromCallable 代替 defer。)** + 8. error(): Again useful to generate error signal. I will share with you when we will discuss about the Observer and there methods. -一个可以弹出错误提示的方法。当我们讨论观察君(Observer 类)和他的方法的时候,我再和你分享吧。 +一个可以弹出错误提示的方法。当我们讨论 Observer 类和他的方法的时候,我再和你分享吧。 9. never(): This API emit nothing. -这个 API 什么泛型都没有。 +这个 API 创建出的 Observable 对象没有包含泛型。**(译者注:Observable.never 虽然可以得到一个 Observable 对象,但是注册的对应 Observer 既不会调用 onNext 方法也不会 onCompleted 方法,甚至不会调用 onError 方法)** Me: Wow. Thank you Observable. For a long and robust answer. I will use that as a cheat sheet for me. Observable can you convert any function as a Observable. -我:哇哦。谢谢你,被观察君。谢谢你耐心又详细的回答,我把你的回答记在我的秘籍手册上的。被观察君,你可以把函数转化 Observable 类吗? +我:哇哦。谢谢你,Observable 类。谢谢你耐心又详细的回答,我把你的回答记在我的秘籍手册上的。话说,你可以把函数也转化 Observable 对象吗? Observable: Yes. Check below code. -当然,注意下面的代码: +Observable 类:当然,注意下面的代码。 ``` public static void main(String[] args) throws InterruptedException { @@ -420,19 +443,19 @@ private static float scale(int width, int height){ Me: Wow you are really powerful. Currently I want to ask you about operators like map, filter and more. But if you want to share with me about Observable creation. Which I am not able to ask you due to lack of knowledge please share with me. -我:哇哦,你真的好流弊。现在我想问你有关操作符,比如 map ,filter方面的问题。但是有关 Observable 类创建,如果还有什么我因为缺乏知识没问到的地方,再多告诉我一点呗。 +我:哇哦,你真的好强大。现在我想问你有关操作符,比如 map ,filter 方面的问题。但是有关 Observable 类创建,如果还有什么我因为缺乏知识没问到的地方,再多告诉我一点呗。 Observable: There is a lot. But I think I can explain here about two types of Observables. One is called Cold Observable and the second one is called Hot Observable. In Cold … -被观察君:其实还有很多。我在这里介绍两类 Observable 类。一种叫做冷被观察者(Cold Observable), 第二个是热被观察者(Hot Observable)。在冷... +Observable 类:其实还有很多。我在这里介绍两类 Observable 类。一种叫做冷被观察者(Cold Observable), 第二个是热被观察者(Hot Observable)。在冷... Conclusion: -结语: +总结: Hello Friends. This dialogue is very very long but I need to stop some where. Otherwise this post will be like a giant book which may be ok but the main purpose will be die and that is, I want we should learn and know everything practically. So I am going to pause my dialogue here I will do resume in next part. Only try your best to play with all these methods and if possible try to take your real world projects and refactor these for practice. In the end I want to say thanks to Rx Observable who give me a lot of his/her time. -大家吼啊。这篇对话非常非常的长,我的暂停一下了。不然这篇文章就会像一部大部头的书,可能看上去不错但是主要目的就跑偏了。我希望,我们可以循序渐进的学习。所以我要暂停我的对话,然后在下一篇继续。你试试自己实现这些方法,如果可能的话在实际的项目中去运用、重构。最后我想说,谢谢被观察君给我了这么多他/她的时间。 +大家好。这篇对话已经非常非常的长,我需要就此搁笔了。不然这篇文章就会像大部头的书,可能看上去不错,但是主要目的就跑偏了。我希望,我们可以循序渐进的学习。所以我要暂停我的对话,然后在下一篇继续。读者可以试试亲自实现这些方法,如果可能的话在实际的项目中去运用、重构。最后我想说,谢谢Observable 类给我了这么多他/她的时间。 Happy Weekend Friends Bye. 🙂 @@ -441,3 +464,4 @@ Happy Weekend Friends Bye. 🙂 --- > [掘金翻译计划](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)、[React](https://github.com/xitu/gold-miner#react)、[前端](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)。 + From ded937c70cd0e5d5ea80c9bcd575a7e36156a340 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ARX-8=5C=E6=AC=A7=E9=98=B3=E5=A4=8F=E6=98=B1?= Date: Sat, 29 Apr 2017 00:23:01 +0800 Subject: [PATCH 285/638] =?UTF-8?q?2017.4.29=20=E4=BF=AE=E6=AD=A3=E4=BA=86?= =?UTF-8?q?=E4=B8=80=E4=BA=9B=E6=A0=BC=E5=BC=8F=E4=B8=8A=E7=9A=84=E9=94=99?= =?UTF-8?q?=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...le-developer-android-rxjava2-hell-part5.md | 54 +++++++------------ 1 file changed, 20 insertions(+), 34 deletions(-) diff --git a/TODO/dialogue-rx-observable-developer-android-rxjava2-hell-part5.md b/TODO/dialogue-rx-observable-developer-android-rxjava2-hell-part5.md index 1c310ee6d4d..17a93e8e004 100644 --- a/TODO/dialogue-rx-observable-developer-android-rxjava2-hell-part5.md +++ b/TODO/dialogue-rx-observable-developer-android-rxjava2-hell-part5.md @@ -6,7 +6,7 @@ ## Dialogue between Rx Observable and a Developer (Me) [ Android RxJava2 ] ( What the hell is this ) Part5 ## -## 开发者( 也就是我 )与Rx Observable 类 [ Android RxJava2 ] ( 这到底是什么? ) 第五部分 ## +## 开发者(也就是我)与Rx Observable 类 [ Android RxJava2 ] ( 这到底是什么?) 第五部分 ## WOW, we got one more day so its time to make this day awesome by learning something new 🙂. @@ -15,7 +15,7 @@ WOW, we got one more day so its time to make this day awesome by learning someth Hello guys, hope you are doing good. This is our fifth post in series of RxJava2 Android [ [part1](http://www.uwanttolearn.com/android/reactive-programming-android-rxjava2-hell-part1/), [part2](http://www.uwanttolearn.com/android/pull-vs-push-imperative-vs-reactive-reactive-programming-android-rxjava2-hell-part2/), [part3](http://www.uwanttolearn.com/android/functional-interfaces-functional-programming-and-lambda-expressions-reactive-programming-android-rxjava2-what-the-hell-is-this-part3/), [part4](http://www.uwanttolearn.com/android/war-learning-curve-rx-java-2-java-8-stream-android-rxjava2-hell-part4/) ]. In this part we are going to work with Rx Java Android. Our prerequisites are done. So we can start now. -大家好,希望你们都过的不错。这是我们 RxJava2 Android 系列的第五篇文章 [ [part1](http://www.uwanttolearn.com/android/reactive-programming-android-rxjava2-hell-part1/), [part2](http://www.uwanttolearn.com/android/pull-vs-push-imperative-vs-reactive-reactive-programming-android-rxjava2-hell-part2/), [part3](http://www.uwanttolearn.com/android/functional-interfaces-functional-programming-and-lambda-expressions-reactive-programming-android-rxjava2-what-the-hell-is-this-part3/), [part4](http://www.uwanttolearn.com/android/war-learning-curve-rx-java-2-java-8-stream-android-rxjava2-hell-part4/) ] 。在这篇文章中,我们会继续研究 Rx Java Android 。 +大家好,希望你们都过的不错。这是我们 RxJava2 Android 系列的第五篇文章 [ [part1](http://www.uwanttolearn.com/android/reactive-programming-android-rxjava2-hell-part1/), [part2](http://www.uwanttolearn.com/android/pull-vs-push-imperative-vs-reactive-reactive-programming-android-rxjava2-hell-part2/), [part3](http://www.uwanttolearn.com/android/functional-interfaces-functional-programming-and-lambda-expressions-reactive-programming-android-rxjava2-what-the-hell-is-this-part3/), [part4](http://www.uwanttolearn.com/android/war-learning-curve-rx-java-2-java-8-stream-android-rxjava2-hell-part4/) ] 。在这篇文章中,我们会继续研究 Rx Java Android 。 **Motivation:** @@ -23,7 +23,7 @@ Hello guys, hope you are doing good. This is our fifth post in series of RxJava2 Motivation is same which I share with you in [part1](http://www.uwanttolearn.com/android/reactive-programming-android-rxjava2-hell-part1/). This time we will see, a lot of things in action which we already learned in last four posts. -动机和我在第一部分 [part1](http://www.uwanttolearn.com/android/reactive-programming-android-rxjava2-hell-part1/) 中分享给大家的一样。现在我们来把之前 4 篇学到的东西融会贯通起来。 +动机和我在第一部分 [part1](http://www.uwanttolearn.com/android/reactive-programming-android-rxjava2-hell-part1/) 中分享给大家的一样。现在我们把之前 4 篇学到的东西融会贯通起来。 **Introduction:** @@ -48,7 +48,7 @@ Me: Why your learning curve is really tough? Why you are not easy for developers Observable: Haha. Truly saying. I really want to make lot of friends instead I have some very good friends. Which discuss about me on different forums and they are talking about me and my powers. These guys are really good in lot of things, they are spending a lot of hours with me. So for a good friend ship you need to give your time with sincerity. But there is one issue, some developers wants to make friendship with me but they are not sincere. They start working on me but after some minutes they open social websites and forgot me about hours. So how you can expect from me, I will be a good friend of a developer who is not sincere with me. -Observable 类:哈哈,你说的是。我真想交很多朋友,不过我现在也有一些好哥们儿。他们在不同的论坛上讨论我,介绍我和我的能力。而且这些家伙真的很棒,他们花了很久的时间和我呆在一起。只有精诚所至,才会金石为开。但问题是,很多想撩我的人只走肾不走心。他们关注我了一小会就去刷推特,把我给忘了。所以说,对我不真诚的人又如何指望我和他们交朋友呢? +Observable 类:哈哈,你说的是。我真想交很多朋友,不过我现在也有一些好哥们儿。他们在不同的论坛上讨论我,介绍我和我的能力。而且这些家伙真的很棒,他们花了很久的时间和我呆在一起。只有精诚所至,才会金石为开。但问题是,很多想撩我的人只走肾不走心。他们关注我了一小会就去刷推特脸书,把我给忘了。所以说,对我不真诚的人又如何指望我和他们交朋友呢? Me: Okay If I want to make a friendship with you. What I will do? @@ -95,9 +95,7 @@ Observable 类:这个问题的答案很长很长。如果你来看我(Rx Jav Every method will return you Observable. Yes I have a lot of friends in my community which I use to make my self according to Developer requirement like map, filter and more but I am here going to share with you some methods which will help you to make any thing as Observable. Sorry because I have a feeling answer will be long but that will not be boring. I will not only show you methods to create Observable instead I will share with you how you refactor your current data objects to Observable with suitable method. -我的团队里也有好几个朋友,可以根据开发者的需求返回 Observable 对象,比如 map ,filter。不过现在我会告诉你几个可以帮助你把任何东西转化为 Observable 类的方法。抱歉我的回答可能会很长,但是也不会很无聊。我不仅仅会演示这些方法如何创建 Observable 类,同时也会和你像你展示如何对手头边代码进行重构。 - -1. just(): +我的团队里也有好几个朋友,可以根据开发者的需求返回 Observable 对象,比如 map ,filter。不过现在我会告诉你几个可以帮助你把任何东西转化为 Observable 对象的方法。抱歉我的回答可能会很长,但是也不会很无聊。我不仅仅会演示这些方法如何创建 Observable 类,同时也会向你展示如何对手头边代码进行重构。 1. just(): @@ -175,9 +173,6 @@ if (!(s != null && !s.isEmpty() && s.length() > 3)) ``` -2. from…: - - 2. from…: I have a lot more API to convert your complex data structure into Observable which starting keyword is from as shown below. @@ -215,7 +210,7 @@ public String toString() {return "Tasks{" + "id=" + id + ", description='" + des From array: -从数组转化为 Observable 类 +从数组转化为 Observable 对象 ``` public static void main(String[] args) { @@ -230,9 +225,6 @@ Here two examples are enough. You can try others on your own. 两个例子就够啦,回头你可以亲自试试其他的。 -3. create(): - - 3. create(): You can define any thing you want as an Observable. This API will give you a lot of power but in my opinion before going to use this API try to search some other solution because I have a feeling 99% times you can get solution from my other API’s. If you are not able to get any solution of something then you can use. @@ -258,9 +250,6 @@ Observable me= Observable.create(new ObservableOnSubscribe() { ``` 4. range(): - - -4. range(): That is just like a for loop as shown below in code. @@ -296,14 +285,11 @@ if(i%2== 0)continue; ``` -5. interval(): - - 5. interval(): This one is awesome. I am showing you one example in which you can compare two approaches. For first one I used a Java thread and for a second one I used my own interval() API and both have same result. -这个 API 碉堡了。我用两种方法实现同一种需求,你可以比较一下。第一种我用 Java 的线程来实现,另一种我用 interval() API ,两种方法会得到同一个结果。 +这个 API 碉堡了。我用两种方法实现同一种需求,你可以比较一下。第一种我用 Java 的线程来实现,另一种我用 interval() 这个 API ,两种方法会得到同一个结果。 **(校对 wbinarytree 注:interval() 会默认在 Scheduler.compute 进行操作。)** @@ -327,9 +313,6 @@ public static void greeting(){ } ``` -6. timer(): - - 6. timer(): One more good API. In program if I want some thing will called after one second I can use timer Observable as shown below. @@ -353,7 +336,7 @@ public static void greeting(){ This is useful specially in mocking. This create Observable that emit nothing and complete. I am showing you one example in which if tests are running then send me mock data else the real one. -这个 API 很有用,尤其是在有假数据的时候。这个 API 创建了一个什么都不包含,只有 complete 方法的 Observable 类 。比如这个例子,如果在测试运行时发送给我假数据,在生产环境下就调用真的数据。 +这个 API 很有用,尤其是在有假数据的时候。这个 API 创建的 Observable 对象中,注册的 Observer 对象只调用 complete 方法。比如这个例子,如果在测试运行时发送给我假数据,在生产环境下就调用真的数据。 ``` public static void main(String[] args) throws InterruptedException { @@ -365,7 +348,7 @@ return isMock ? Observable.empty(): Observable.just(1, 2, 3, 4); } ``` -7. defer(): +8. defer(): This is very use full in many cases. I am going to explain this one by using one example as shown below. @@ -394,7 +377,7 @@ private static class Employee{ What will be the output of the above code. If your answer is age should be 28 then you are wrong. Basically all creation methods of Observable will take the value which is available at the time of creation. Like if I do output I will get 27 because I create an Observable at that time when I have age 27 and later I change to 28 but observable already created. So what will be the solution? Yes you can use defer API. That is really helpful. When you use defer basically what happen Observable only created when you will subscribe so its mean by using this I will get my expected result. -上面的代码会输出什么呢?如果你的答案是 age= 28 那就大错特错了。基本上所有创建 Observable 的方法在创建时就记录了可用的值。就像刚才的数据实际上输出的是 age = 27 , 因为在我创建 Observable 的时候 age 值是 27 ,当我把 age 的值变成 28 的时候 Observable 类已经创建过了。所以怎么解决这个问题呢?是的,这个时候就轮到 defer 这个 API 出场了。太有用了!当你使用 defer 以后,只有注册(subscribe)的时候才创建 Observable 类。用这个 API ,我就可以获得想要的值。 +上面的代码会输出什么呢?如果你的答案是 age = 28 那就大错特错了。基本上所有创建 Observable 对象的方法在创建时就记录了可用的值。就像刚才的数据实际上输出的是 age = 27 , 因为在我创建 Observable 的时候 age 值是 27 ,当我把 age 的值变成 28 的时候 Observable 类已经创建过了。所以怎么解决这个问题呢?是的,这个时候就轮到 defer 这个 API 出场了。太有用了!当你使用 defer 以后,只有注册(subscribe)的时候才创建 Observable 类。用这个 API ,我就可以获得想要的值。 ``` Observable getObservable(){ @@ -409,21 +392,24 @@ Now this time my age on output is 28. **(校对 wbinarytree 注:Observable 的创建方法中,并不是像原文中写到的,“基本上所有创建 Observable 的方法在创建时就记录了可用的值”。而是只有 just, from 方法。 create , fromCallable 等等方法都是在 subscribe 后才会调用。文中的例子可以使用 fromCallable 代替 defer。)** -8. error(): +9. error(): Again useful to generate error signal. I will share with you when we will discuss about the Observer and there methods. 一个可以弹出错误提示的方法。当我们讨论 Observer 类和他的方法的时候,我再和你分享吧。 -9. never(): +10. never(): + This API emit nothing. -这个 API 创建出的 Observable 对象没有包含泛型。**(译者注:Observable.never 虽然可以得到一个 Observable 对象,但是注册的对应 Observer 既不会调用 onNext 方法也不会 onCompleted 方法,甚至不会调用 onError 方法)** +这个 API 创建出的 Observable 对象没有包含泛型。 + +**(译者注:Observable.never 虽然可以得到一个 Observable 对象,但是注册的对应 Observer 既不会调用 onNext 方法也不会 onCompleted 方法,甚至不会调用 onError 方法)** Me: Wow. Thank you Observable. For a long and robust answer. I will use that as a cheat sheet for me. Observable can you convert any function as a Observable. -我:哇哦。谢谢你,Observable 类。谢谢你耐心又详细的回答,我把你的回答记在我的秘籍手册上的。话说,你可以把函数也转化 Observable 对象吗? +我:哇哦。谢谢你,Observable 类。谢谢你耐心又详细的回答,我会把你的回答记在我的秘籍手册上的。话说,你可以把函数也转化成 Observable 对象吗? Observable: Yes. Check below code. @@ -443,11 +429,11 @@ private static float scale(int width, int height){ Me: Wow you are really powerful. Currently I want to ask you about operators like map, filter and more. But if you want to share with me about Observable creation. Which I am not able to ask you due to lack of knowledge please share with me. -我:哇哦,你真的好强大。现在我想问你有关操作符,比如 map ,filter 方面的问题。但是有关 Observable 类创建,如果还有什么我因为缺乏知识没问到的地方,再多告诉我一点呗。 +我:哇哦,你真的好强大。现在我想问你有关操作符,比如 map ,filter 方面的问题。但是有关 Observable 对象创建,如果还有什么我因为缺乏知识没问到的地方,再多告诉我一点呗。 Observable: There is a lot. But I think I can explain here about two types of Observables. One is called Cold Observable and the second one is called Hot Observable. In Cold … -Observable 类:其实还有很多。我在这里介绍两类 Observable 类。一种叫做冷被观察者(Cold Observable), 第二个是热被观察者(Hot Observable)。在冷... +Observable 类:其实还有很多。我在这里介绍两类 Observable 对象。一种叫做 Cold Observable,第二个是 Hot Observable。在... Conclusion: @@ -455,7 +441,7 @@ Conclusion: Hello Friends. This dialogue is very very long but I need to stop some where. Otherwise this post will be like a giant book which may be ok but the main purpose will be die and that is, I want we should learn and know everything practically. So I am going to pause my dialogue here I will do resume in next part. Only try your best to play with all these methods and if possible try to take your real world projects and refactor these for practice. In the end I want to say thanks to Rx Observable who give me a lot of his/her time. -大家好。这篇对话已经非常非常的长,我需要就此搁笔了。不然这篇文章就会像大部头的书,可能看上去不错,但是主要目的就跑偏了。我希望,我们可以循序渐进的学习。所以我要暂停我的对话,然后在下一篇继续。读者可以试试亲自实现这些方法,如果可能的话在实际的项目中去运用、重构。最后我想说,谢谢Observable 类给我了这么多他/她的时间。 +大家好。这篇对话已经非常非常的长,我需要就此搁笔了。不然这篇文章就会像大部头的书,可能看上去不错,但是主要目的就跑偏了。我希望,我们可以循序渐进的学习。所以我要暂停我的对话,然后在下一篇继续。读者可以试试亲自实现这些方法,如果可能的话在实际的项目中去运用、重构。最后我想说,谢谢 Observable 类给我了这么多他/她的时间。 Happy Weekend Friends Bye. 🙂 From 79466fb8d832a6926621b9ed1e7801e5332e7358 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ARX-8=5C=E6=AC=A7=E9=98=B3=E5=A4=8F=E6=98=B1?= Date: Sat, 29 Apr 2017 00:32:11 +0800 Subject: [PATCH 286/638] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E9=A2=98=E7=9B=AE?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ogue-rx-observable-developer-android-rxjava2-hell-part5.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TODO/dialogue-rx-observable-developer-android-rxjava2-hell-part5.md b/TODO/dialogue-rx-observable-developer-android-rxjava2-hell-part5.md index 17a93e8e004..0de461c4006 100644 --- a/TODO/dialogue-rx-observable-developer-android-rxjava2-hell-part5.md +++ b/TODO/dialogue-rx-observable-developer-android-rxjava2-hell-part5.md @@ -6,7 +6,7 @@ ## Dialogue between Rx Observable and a Developer (Me) [ Android RxJava2 ] ( What the hell is this ) Part5 ## -## 开发者(也就是我)与Rx Observable 类 [ Android RxJava2 ] ( 这到底是什么?) 第五部分 ## +## 开发者(也就是我)与Rx Observable 类的对话 [ Android RxJava2 ] ( 这到底是什么?) 第五部分 ## WOW, we got one more day so its time to make this day awesome by learning something new 🙂. @@ -291,7 +291,7 @@ This one is awesome. I am showing you one example in which you can compare two a 这个 API 碉堡了。我用两种方法实现同一种需求,你可以比较一下。第一种我用 Java 的线程来实现,另一种我用 interval() 这个 API ,两种方法会得到同一个结果。 -**(校对 wbinarytree 注:interval() 会默认在 Scheduler.compute 进行操作。)** +**(校对 wbinarytree 注:interval() 会默认在 Scheduler.computation() 进行操作。)** ``` public static void main(String[] args) { From 55d039aa56ba40e705d29bb2bdf2fd1fc632b8ca Mon Sep 17 00:00:00 2001 From: sqrthree Date: Sat, 29 Apr 2017 00:36:56 +0800 Subject: [PATCH 287/638] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20=E7=9C=9F=E6=AD=A3?= =?UTF-8?q?=E8=A1=8C=E5=8A=A8=E4=B9=8B=E5=89=8D=20=E4=BD=A0=E5=B0=86?= =?UTF-8?q?=E4=B8=80=E6=97=A0=E6=89=80=E6=88=90=20=E7=9A=84=E7=A7=AF?= =?UTF-8?q?=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- front-end.md | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 446aa225057..4126c02153a 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,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) 上的英文分享文章。内容覆盖 [Android](#android)、[iOS](#ios)、[React](#react)、[前端](#前端)、[后端](#后端)、[产品](#产品)、[设计](#设计) 等领域,读者为热爱新技术的新锐开发者。 -掘金翻译计划目前翻译完成 [474](#近期文章列表) 篇文章,共有 [300](https://github.com/xitu/gold-miner/wiki/%E8%AF%91%E8%80%85%E7%A7%AF%E5%88%86%E8%A1%A8) 余名译者贡献翻译。 +掘金翻译计划目前翻译完成 [475](#近期文章列表) 篇文章,共有 [300](https://github.com/xitu/gold-miner/wiki/%E8%AF%91%E8%80%85%E7%A7%AF%E5%88%86%E8%A1%A8) 余名译者贡献翻译。 # 官方指南: @@ -37,10 +37,10 @@ ## 前端 +* [真正行动之前 你将一无所成](https://juejin.im/entry/58f6136861ff4b00580aef28?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([owenlyn](https://github.com/owenlyn) 翻译) * [高阶函数(软件编写)(第四部分)](https://juejin.im/post/58f6d6ff570c3500564fbddc?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([reid3290](https://github.com/reid3290) 翻译) * [为什么用 JavaScript 学习函数式编程?(软件编写)(第二部分)](https://juejin.im/post/58f5a2ecb123db2fa2b1b244?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([gy134340](http://gy134340.com/) 翻译) * [Functor 与 Category (软件编写)(第六部分)](https://juejin.im/post/58f58d5da0bb9f006aac3e8d?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([yoyoyohamapi](https://github.com/yoyoyohamapi) 翻译) -* [函数式程序员的 JavaScript 简介 (软件编写)(第三部分)](https://juejin.im/post/58f58b06da2f60005d43388b?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([sunui](https://github.com/sunui) 翻译) * [所有前端译文>>](https://github.com/xitu/gold-miner/blob/master/front-end.md) diff --git a/front-end.md b/front-end.md index 7d87d37fb04..ca358bbcfb6 100644 --- a/front-end.md +++ b/front-end.md @@ -1,3 +1,4 @@ +* [真正行动之前 你将一无所成](https://juejin.im/entry/58f6136861ff4b00580aef28?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([owenlyn](https://github.com/owenlyn) 翻译) * [高阶函数(软件编写)(第四部分)](https://juejin.im/post/58f6d6ff570c3500564fbddc?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([reid3290](https://github.com/reid3290) 翻译) * [为什么用 JavaScript 学习函数式编程?(软件编写)(第二部分)](https://juejin.im/post/58f5a2ecb123db2fa2b1b244?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([gy134340](http://gy134340.com/) 翻译) * [Functor 与 Category (软件编写)(第六部分)](https://juejin.im/post/58f58d5da0bb9f006aac3e8d?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([yoyoyohamapi](https://github.com/yoyoyohamapi) 翻译) From e600f226e7c57d7855c301c0a0ae4596b577d6cb Mon Sep 17 00:00:00 2001 From: sqrthree Date: Sat, 29 Apr 2017 00:51:01 +0800 Subject: [PATCH 288/638] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20=E4=BB=8E=E5=BD=A2?= =?UTF-8?q?=E5=BC=8F=E5=88=B0=E5=8A=9F=E8=83=BD=EF=BC=8C=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E6=80=9D=E7=BB=B4=E7=9A=84=E6=94=B9=E5=8F=98=20=E7=9A=84?= =?UTF-8?q?=E7=A7=AF=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- design.md | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4126c02153a..02cd0d820a2 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,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) 上的英文分享文章。内容覆盖 [Android](#android)、[iOS](#ios)、[React](#react)、[前端](#前端)、[后端](#后端)、[产品](#产品)、[设计](#设计) 等领域,读者为热爱新技术的新锐开发者。 -掘金翻译计划目前翻译完成 [475](#近期文章列表) 篇文章,共有 [300](https://github.com/xitu/gold-miner/wiki/%E8%AF%91%E8%80%85%E7%A7%AF%E5%88%86%E8%A1%A8) 余名译者贡献翻译。 +掘金翻译计划目前翻译完成 [476](#近期文章列表) 篇文章,共有 [300](https://github.com/xitu/gold-miner/wiki/%E8%AF%91%E8%80%85%E7%A7%AF%E5%88%86%E8%A1%A8) 余名译者贡献翻译。 # 官方指南: @@ -72,10 +72,10 @@ ## 设计 +* [从形式到功能,设计思维的改变](https://juejin.im/post/58fedca744d9040069f720e4/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([Ruixi](https://github.com/Ruixi) 翻译) * [搜索结果页的最佳实践](https://juejin.im/post/58da37c761ff4b00607287a6/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([sunui](https://github.com/sunui) 翻译) * [某些2017年的 UX 趋势啊,扎心了](https://juejin.im/post/58cf5dc22f301e007e52fb2b/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([Ruixi](https://github.com/Ruixi) 翻译) * [像开发人员一样做设计](https://gold.xitu.io/entry/58b7ba6f8fd9c56d16be6bb0/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([Airmacho](https://github.com/Airmacho) 翻译) -* [设计预期的用户体验](https://gold.xitu.io/entry/58b2d8e9570c3500696f53a5/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([jifaxu](https://github.com/jifaxu) 翻译) * [所有设计译文>>](https://github.com/xitu/gold-miner/blob/master/design.md) diff --git a/design.md b/design.md index d2d30a65ad9..d0fe601d5ff 100644 --- a/design.md +++ b/design.md @@ -1,3 +1,4 @@ +* [从形式到功能,设计思维的改变](https://juejin.im/post/58fedca744d9040069f720e4/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([Ruixi](https://github.com/Ruixi) 翻译) * [搜索结果页的最佳实践](https://juejin.im/post/58da37c761ff4b00607287a6/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([sunui](https://github.com/sunui) 翻译) * [某些2017年的 UX 趋势啊,扎心了](https://juejin.im/post/58cf5dc22f301e007e52fb2b/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([Ruixi](https://github.com/Ruixi) 翻译) * [像开发人员一样做设计](https://gold.xitu.io/entry/58b7ba6f8fd9c56d16be6bb0/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([Airmacho](https://github.com/Airmacho) 翻译) From 835792ad70c281af98fbfd40f3cb9210a701cccd Mon Sep 17 00:00:00 2001 From: sqrthree Date: Sat, 29 Apr 2017 01:02:45 +0800 Subject: [PATCH 289/638] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20Node.js=20?= =?UTF-8?q?=E4=B9=8B=E6=88=98:=20=E5=A6=82=E4=BD=95=E5=9C=A8=E7=94=9F?= =?UTF-8?q?=E4=BA=A7=E7=8E=AF=E5=A2=83=E4=B8=AD=E8=B0=83=E8=AF=95=E9=94=99?= =?UTF-8?q?=E8=AF=AF=20=E7=9A=84=E7=A7=AF=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- backend.md | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 02cd0d820a2..f0e341660f1 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,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) 上的英文分享文章。内容覆盖 [Android](#android)、[iOS](#ios)、[React](#react)、[前端](#前端)、[后端](#后端)、[产品](#产品)、[设计](#设计) 等领域,读者为热爱新技术的新锐开发者。 -掘金翻译计划目前翻译完成 [476](#近期文章列表) 篇文章,共有 [300](https://github.com/xitu/gold-miner/wiki/%E8%AF%91%E8%80%85%E7%A7%AF%E5%88%86%E8%A1%A8) 余名译者贡献翻译。 +掘金翻译计划目前翻译完成 [477](#近期文章列表) 篇文章,共有 [300](https://github.com/xitu/gold-miner/wiki/%E8%AF%91%E8%80%85%E7%A7%AF%E5%88%86%E8%A1%A8) 余名译者贡献翻译。 # 官方指南: @@ -55,10 +55,10 @@ ## 后端 +* [Node.js 之战: 如何在生产环境中调试错误](https://juejin.im/post/59035d3644d904006919086b/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([mnikn](https://github.com/mnikn)翻译) * [我经常听到的 GraphQL 到底是什么?](https://juejin.im/post/58fd6d121b69e600589ec740/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([lsvih](https://github.com/lsvih)翻译) * [系统设计入门](https://github.com/xitu/system-design-primer/blob/master/README-zh-Hans.md) ([XatMassacrE](https://github.com/XatMassacrE) [L9m](https://github.com/L9m) [Airmacho](https://github.com/Airmacho) [xiaoyusilen](https://github.com/xiaoyusilen) [jifaxu](https://github.com/jifaxu)翻译) * [如何使用 HTTP Headers 来保护你的 Web 应用](https://juejin.im/post/58f5d3718d6d810057c18f75/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([bambooom](https://github.com/bambooom)翻译) -* [解析 Go 中的函数调用](https://juejin.im/post/58f579b58d6d81006491c7c0/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([xiaoyusilen](https://github.com/xiaoyusilen)翻译) * [所有后端译文>>](https://github.com/xitu/gold-miner/blob/master/backend.md) ## 教程 diff --git a/backend.md b/backend.md index 00c853f6052..ad3cb698260 100644 --- a/backend.md +++ b/backend.md @@ -1,3 +1,4 @@ +* [Node.js 之战: 如何在生产环境中调试错误](https://juejin.im/post/59035d3644d904006919086b/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([mnikn](https://github.com/mnikn)翻译) * [我经常听到的 GraphQL 到底是什么?](https://juejin.im/post/58fd6d121b69e600589ec740/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([lsvih](https://github.com/lsvih)翻译) * [系统设计入门](https://github.com/xitu/system-design-primer/blob/master/README-zh-Hans.md) ([XatMassacrE](https://github.com/XatMassacrE) [L9m](https://github.com/L9m) [Airmacho](https://github.com/Airmacho) [xiaoyusilen](https://github.com/xiaoyusilen) [jifaxu](https://github.com/jifaxu)翻译) * [如何使用 HTTP Headers 来保护你的 Web 应用](https://juejin.im/post/58f5d3718d6d810057c18f75/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([bambooom](https://github.com/bambooom)翻译) From 98327a557b5e0902e4ede3b52ad8c8fb9718a78a Mon Sep 17 00:00:00 2001 From: sqrthree Date: Sat, 29 Apr 2017 01:52:54 +0800 Subject: [PATCH 290/638] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20RxJava=20=E4=B8=AD?= =?UTF-8?q?=E7=9A=84=E5=A4=9A=E7=BA=BF=E7=A8=8B=20=E7=9A=84=E7=A7=AF?= =?UTF-8?q?=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- android.md | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f0e341660f1..b61e5529b81 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,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) 上的英文分享文章。内容覆盖 [Android](#android)、[iOS](#ios)、[React](#react)、[前端](#前端)、[后端](#后端)、[产品](#产品)、[设计](#设计) 等领域,读者为热爱新技术的新锐开发者。 -掘金翻译计划目前翻译完成 [477](#近期文章列表) 篇文章,共有 [300](https://github.com/xitu/gold-miner/wiki/%E8%AF%91%E8%80%85%E7%A7%AF%E5%88%86%E8%A1%A8) 余名译者贡献翻译。 +掘金翻译计划目前翻译完成 [478](#近期文章列表) 篇文章,共有 [300](https://github.com/xitu/gold-miner/wiki/%E8%AF%91%E8%80%85%E7%A7%AF%E5%88%86%E8%A1%A8) 余名译者贡献翻译。 # 官方指南: @@ -20,10 +20,10 @@ ## Android +* [RxJava 中的多线程](https://juejin.im/post/58ff6259da2f60005dd81459/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([phxnirvana](https://github.com/phxnirvana) 翻译) * [Android 如何实现气泡选择动画](https://juejin.im/post/58e5ec838d6d8100616d82e2/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([skyar2009](https://github.com/skyar2009) 翻译) * [一个人的 Android 开发](https://juejin.im/entry/58dca515b123db00603887fd/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([BoilerYao](https://github.com/BoilerYao) 翻译) * [我是如何做到在 5 分钟之内将应用大小减少 60% 的](https://juejin.im/post/58d9b6a1a22b9d0064719f9e/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([jifaxu](https://github.com/jifaxu) 翻译) -* [拉模式和推模式,命令式和响应式 – 响应式编程 [Android RxJava2](这到底是什么):第二部分](https://juejin.im/entry/58d78547a22b9d006465ca57/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([XHShirley](https://github.com/XHShirley) 翻译) * [所有 Android 译文>>](https://github.com/xitu/gold-miner/blob/master/android.md) diff --git a/android.md b/android.md index 8b02a079727..5f71c32ddf2 100644 --- a/android.md +++ b/android.md @@ -1,3 +1,4 @@ +* [RxJava 中的多线程](https://juejin.im/post/58ff6259da2f60005dd81459/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([phxnirvana](https://github.com/phxnirvana) 翻译) * [Android 如何实现气泡选择动画](https://juejin.im/post/58e5ec838d6d8100616d82e2/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([skyar2009](https://github.com/skyar2009) 翻译) * [一个人的 Android 开发](https://juejin.im/entry/58dca515b123db00603887fd/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([BoilerYao](https://github.com/BoilerYao) 翻译) * [我是如何做到在 5 分钟之内将应用大小减少 60% 的](https://juejin.im/post/58d9b6a1a22b9d0064719f9e/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([jifaxu](https://github.com/jifaxu) 翻译) From fdf40571ca2545a75f5ef3eed287e4a5100f2e56 Mon Sep 17 00:00:00 2001 From: sqrthree Date: Sat, 29 Apr 2017 02:02:08 +0800 Subject: [PATCH 291/638] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20=E9=92=88=E5=AF=B9?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5=E8=80=85=E7=9A=84=E4=BD=93=E9=AA=8C=E8=AE=BE?= =?UTF-8?q?=E8=AE=A1=20=E7=9A=84=E7=A7=AF=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- product.md | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b61e5529b81..91ab3de158d 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,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) 上的英文分享文章。内容覆盖 [Android](#android)、[iOS](#ios)、[React](#react)、[前端](#前端)、[后端](#后端)、[产品](#产品)、[设计](#设计) 等领域,读者为热爱新技术的新锐开发者。 -掘金翻译计划目前翻译完成 [478](#近期文章列表) 篇文章,共有 [300](https://github.com/xitu/gold-miner/wiki/%E8%AF%91%E8%80%85%E7%A7%AF%E5%88%86%E8%A1%A8) 余名译者贡献翻译。 +掘金翻译计划目前翻译完成 [479](#近期文章列表) 篇文章,共有 [300](https://github.com/xitu/gold-miner/wiki/%E8%AF%91%E8%80%85%E7%A7%AF%E5%88%86%E8%A1%A8) 余名译者贡献翻译。 # 官方指南: @@ -81,8 +81,8 @@ ## 产品 +* [针对失败者的体验设计](https://juejin.im/post/59013f6eda2f60005de40516/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([ylq167](https://github.com/ylq167) 翻译) * [细节是产品设计的重中之重](https://juejin.im/post/58ed96aaa22b9d00634732e9/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([iloveivyxuan](https://github.com/iloveivyxuan) 翻译) * [单元测试,精益创业,以及两者之间的关系](https://juejin.im/post/58d90a3b44d90400694505c4/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([gy134340](http://gy134340.com/) 翻译) * [你正在阅读的用户体验文章是不是在向你进行推销?](https://juejin.im/post/58d4c501a22b9d00645544d9/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([ylq167](https://github.com/ylq167) 翻译) -* [直观设计 vs. 共享式设计](https://gold.xitu.io/entry/5862650a128fe1006d04d398/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([Funtrip](https://www.behance.net/Funtrip) 翻译) * [所有产品译文>>](https://github.com/xitu/gold-miner/blob/master/product.md) diff --git a/product.md b/product.md index be018fddc3b..b820c315e86 100644 --- a/product.md +++ b/product.md @@ -1,3 +1,4 @@ +* [针对失败者的体验设计](https://juejin.im/post/59013f6eda2f60005de40516/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([ylq167](https://github.com/ylq167) 翻译) * [细节是产品设计的重中之重](https://juejin.im/post/58ed96aaa22b9d00634732e9/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([iloveivyxuan](https://github.com/iloveivyxuan) 翻译) * [单元测试,精益创业,以及两者之间的关系](https://juejin.im/post/58d90a3b44d90400694505c4/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([gy134340](http://gy134340.com/) 翻译) * [你正在阅读的用户体验文章是不是在向你进行推销?](https://juejin.im/post/58d4c501a22b9d00645544d9/?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([ylq167](https://github.com/ylq167) 翻译) From 4e7982820203d521e095beb6b72258d74ac686d4 Mon Sep 17 00:00:00 2001 From: sqrthree Date: Sat, 29 Apr 2017 02:11:03 +0800 Subject: [PATCH 292/638] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E4=B8=AD=E6=B7=BB=E5=8A=A0=E6=B3=A8=E9=87=8A=E4=B9=8B=E5=A5=BD?= =?UTF-8?q?=E5=9D=8F=E4=B8=91=20=E7=9A=84=E7=A7=AF=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- front-end.md | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 91ab3de158d..5edf351859a 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,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) 上的英文分享文章。内容覆盖 [Android](#android)、[iOS](#ios)、[React](#react)、[前端](#前端)、[后端](#后端)、[产品](#产品)、[设计](#设计) 等领域,读者为热爱新技术的新锐开发者。 -掘金翻译计划目前翻译完成 [479](#近期文章列表) 篇文章,共有 [300](https://github.com/xitu/gold-miner/wiki/%E8%AF%91%E8%80%85%E7%A7%AF%E5%88%86%E8%A1%A8) 余名译者贡献翻译。 +掘金翻译计划目前翻译完成 [480](#近期文章列表) 篇文章,共有 [300](https://github.com/xitu/gold-miner/wiki/%E8%AF%91%E8%80%85%E7%A7%AF%E5%88%86%E8%A1%A8) 余名译者贡献翻译。 # 官方指南: @@ -37,10 +37,10 @@ ## 前端 +* [代码中添加注释之好坏丑](https://juejin.im/post/5902126aa0bb9f0065e80ea9?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([bambooom](https://github.com/bambooom) 翻译) * [真正行动之前 你将一无所成](https://juejin.im/entry/58f6136861ff4b00580aef28?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([owenlyn](https://github.com/owenlyn) 翻译) * [高阶函数(软件编写)(第四部分)](https://juejin.im/post/58f6d6ff570c3500564fbddc?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([reid3290](https://github.com/reid3290) 翻译) * [为什么用 JavaScript 学习函数式编程?(软件编写)(第二部分)](https://juejin.im/post/58f5a2ecb123db2fa2b1b244?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([gy134340](http://gy134340.com/) 翻译) -* [Functor 与 Category (软件编写)(第六部分)](https://juejin.im/post/58f58d5da0bb9f006aac3e8d?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([yoyoyohamapi](https://github.com/yoyoyohamapi) 翻译) * [所有前端译文>>](https://github.com/xitu/gold-miner/blob/master/front-end.md) diff --git a/front-end.md b/front-end.md index ca358bbcfb6..1003c4e3da8 100644 --- a/front-end.md +++ b/front-end.md @@ -1,3 +1,4 @@ +* [代码中添加注释之好坏丑](https://juejin.im/post/5902126aa0bb9f0065e80ea9?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([bambooom](https://github.com/bambooom) 翻译) * [真正行动之前 你将一无所成](https://juejin.im/entry/58f6136861ff4b00580aef28?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([owenlyn](https://github.com/owenlyn) 翻译) * [高阶函数(软件编写)(第四部分)](https://juejin.im/post/58f6d6ff570c3500564fbddc?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([reid3290](https://github.com/reid3290) 翻译) * [为什么用 JavaScript 学习函数式编程?(软件编写)(第二部分)](https://juejin.im/post/58f5a2ecb123db2fa2b1b244?utm_source=gold-miner&utm_medium=readme&utm_campaign=github) ([gy134340](http://gy134340.com/) 翻译) From 5943ba67868b9d82ec4626cd1a2320cbfb1a8a59 Mon Sep 17 00:00:00 2001 From: AngryD Date: Sat, 29 Apr 2017 09:41:14 +0800 Subject: [PATCH 293/638] Update testing-views-in-isolation-with-espresso.md --- ...esting-views-in-isolation-with-espresso.md | 44 +++++++++---------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/TODO/testing-views-in-isolation-with-espresso.md b/TODO/testing-views-in-isolation-with-espresso.md index 77dde884ee5..1bc0345f1d1 100644 --- a/TODO/testing-views-in-isolation-with-espresso.md +++ b/TODO/testing-views-in-isolation-with-espresso.md @@ -2,21 +2,21 @@ > * 原文作者:[Ataul Munim](https://www.novoda.com/blog/author/ataulm/) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 译者:[yazhi1992](https://github.com/yazhi1992) -> * 校对者: +> * 校对者:[lovexiaov](https://github.com/lovexiaov), [Phoenix](https://github.com/wbinarytree) # 使用 Espresso 隔离测试视图 # -在这篇文章里,我将会告诉你为何并且如何使用 Espresso 在真机上测试你的自定义视图。 +在这篇文章里,我将会告诉你为何并且如何使用 Espresso 在 Android 设备上测试你的自定义视图。 你可以使用 Espresso 来一次性测试所有界面或流程。这些测试用例会启动某个页面,并像用户一般执行操作,包括等待数据的加载或跳转到其他页面。 -这样做是非常有用的,因为你需要端到端的测试用例来验证常见的用户使用流程。这些自动化测试应该定期地执行,从而确保你有足够的 QA 时间进行探索性测试。 +这样做是非常有用的,因为你需要端到端的测试用例来验证常见的用户使用流程。这些自动化测试应该定期地执行,从而可以节约手工 QA 的时间来进行探索性测试。 即便如此,这些不是可以频繁运行的测试。运行一整套可能会花费数小时的时间(想象一下验证媒体内容的脱机同步),所以你可以选择在夜间运行它们。 -这很困难,因为这些类型的测试包含了多个潜在的故障点。理论上来说,当某个测试失败时,你会希望它是由于单个逻辑断言而导致的。 +这很困难,因为这些类型的测试包含了多个潜在的故障点。理想情况是,当某个测试失败时,你会希望它是由于单个逻辑断言而导致的。 -大多数(或者说很多)被引入的问题是出现在 UI 中的。这些问题很可能是十分细微的,以至于我们在添加新特性时并不会注意到,但是敏锐的 QA 团队却往往可以。 +大多数(或者说很多)可以引入的回归测试点都在 UI 上。这些问题很可能是十分细微的,以至于我们在添加新特性时并不会注意到,但是敏锐的 QA 团队却往往可以。 这样就浪费太多时间了。 @@ -50,46 +50,46 @@ public class MovieItemView extends RelativeLayout { 他们将 UI 的逻辑部分组合在一起,并且通常还包含来自业务领域的命名规范。在 Novoda 的页面布局中你很少会看到“原始”的 Android 视图。 -让我们使用 BDD 风格来编写这些视图测试(译者注:BDD(Behaviour Driven Development)是TDD的一种,倾向于断言被测对象的行为特征而非输入输出),比如“给定的 MovieItemView 被绑定到 Edward Scissorhands 上,然后标题就被设置成 Edward Scissorhands”或者“给定的 MovieItemView 被绑定到 Edward Scissorhands 上,当点击视图时,onClick(Edward Scissorhands) 就会被调用”,等等。 +让我们使用 BDD 风格来编写这些视图测试,比如“当 MovieItemView 被绑定到 Edward Scissorhands 上,标题就被设置成 Edward Scissorhands”或者“MovieItemView 被绑定到 Edward Scissorhands 上,当点击视图时,onClick(Edward Scissorhands) 就会被调用”,等等。(译者注:BDD(Behaviour Driven Development),倾向于断言被测对象的行为特征而非输入输出。一个典型的 BDD 的测试用例包活完整的三段式上下文,测试大多可以翻译为 `Given-When-Then` 的格式,即某种场景下,发生了事件,导致了什么结果。) ## 难道不能使用单元测试来捕获这些问题吗? ## -如果使用了 MVP 或者 MVVM 等表现模式,为什么还需要 Espresso 来运行这些测试呢?为何不直接使用单元测试? +如果你正在使用像 MVP 或者 MVVM 这样可被单元测试的表现模式,为什么还需要 Espresso 来运行这些测试呢? 首先,让我们来看一下展示信息的流程并且描述一下目前所能做的测试,然后再看看使用 Espresso 测试能多做些什么。 - Presenters 订阅发送事件的数据生成器 -- 事件可以是‘加载’,‘空闲’或‘错误’,并且可能带有要展示的数据 +- 事件可以处于`加载中`,`空闲`或`错误`状态,并且可能带有要展示的数据 -- Presenters 将使用`display(List )`,`displayCachedDataWhileLoading(List )`或`displayEmptyScreen()`等方法将这些事件转发给“displayers”(MVP中的“View”)。 +- Presenters 将使用 `display(List)`,`displayCachedDataWhileLoading(List)` 或 `displayEmptyScreen()` 等方法将这些事件转发给“displayers”(MVP 中的“View”)。 -- displayers 的具体实现将显示/隐藏 Android 视图,并执行诸如`moviesView.bind(List ) `之类的操作 +- displayers 的具体实现类将显示/隐藏 Android 视图,并执行诸如 `moviesView.bind(List)` 之类的操作 你可以对 presenters 进行单元测试,验证是否调用了 displayers 正确的方法并且带有正确的参数。 -你可以用相同的方式测试 displayers 吗?是的,你是可以模拟 Android 视图,并验证是否调用了正确的方法。但它不会是正确的粒度: +你可以用相同的方式测试 displayers 吗?是的,你是可以模拟 Android 视图,并验证是否调用了正确的方法。但这样的粒度并不是我们想要的: - displayer 可能确实构建或更新了 RecyclerView 或 ViewPager 适配器,但这并不代表显示了正确的内容。 -- Android 视图是通过在代码中加载 XML(布局和样式)设置的;验证方法的调用不足以断言显示的内容是否正确 +- Android 视图是通过在代码中加载 XML(布局和样式)设置的;验证方法的调用不足以断言显示的内容是否正确 ## 设置测试用例 ## -就从使用 [`espresso-support`](https://github.com/novoda/spikes/tree/master/espresso-support) 这个库开始吧。 +就从使用 [`espresso-support`](https://github.com/novoda/spikes/tree/master/espresso-support) 这个库开始吧。 -在你的 build.gradle (JCenter 可用)里添加依赖 +在你的 build.gradle(JCenter 可用)里添加依赖 ``` debugCompile 'com.novoda:espresso-support-extras:0.0.3' androidTestCompile 'com.novoda:espresso-support:0.0.3' ``` -`extras` 依赖包中包含了 `ViewActivity`,在测试时需要将其添加到你的应用中。你可以在该界面保存想要使用 Espresso 测试的视图。 +`extras` 依赖包中包含了 `ViewActivity`,在测试时需要将其添加到你的应用中。你可以在该 Activity 持有想要使用 Espresso 测试的单一视图。 核心部分(包含自定义测试规则)只需要作为 `androidTest` 依赖中的一部分。 -`ViewTestRule` 使用方法与 `ActivityTestRule` 类似。只不过是将传递的参数从想要启动的Activity类替换成了包含你想要测试的视图的布局文件: +`ViewTestRule` 使用方法与 `ActivityTestRule` 类似。只不过是将传递的参数从想要启动的 Activity 类替换成了包含你想要测试的视图的布局文件: ``` @RunWith(AndroidJUnit4.class)publicclassMovieItemViewTest{ @@ -98,9 +98,9 @@ androidTestCompile 'com.novoda:espresso-support:0.0.3' ... ``` -你可以使用 `ViewTestRule ` 指定根布局的视图类型。 +你可以使用 `ViewTestRule` 指定根布局的视图类型。 -`ViewTestRule` 继承了 `ActivityTestRule `,所以它总会打开 `ViewActivity`。 `getActivityIntent()` 被重写了,所以你可以像使用 Intent 一样将 `R.layout.test_movie_item_view` 传递给`ViewActivity` 。 +`ViewTestRule` 继承了 `ActivityTestRule`,所以它总会打开 `ViewActivity`。 `getActivityIntent()` 被重写了,所以你可以将 `R.layout.test_movie_item_view` 作为 Intent 的附加数据传递给 `ViewActivity`。 你可以在测试中使用 Mockito 代替回调函数。 @@ -119,7 +119,7 @@ publicvoidsetUp(){ } ``` -ViewTestRule 有一个方法 `bindViewUsing(Binder)`,该方法会返回视图的引用,以便你与之进行交互。当你使用 `viewTestRule.getView()` 直接访问视图时,你会希望与视图的所有交互都是在主线程上执行的,而非测试线程。 +ViewTestRule 有一个 `bindViewUsing(Binder)` 方法,该方法会返回视图的引用,以便你与之进行交互。当你使用 `viewTestRule.getView()` 直接访问视图时,你会希望与视图的所有交互都是在主线程上执行的,而非测试线程。 ``` @Before @@ -143,8 +143,7 @@ public void setUp() { - 响应用户的操作 -要为这两种情况编写测试,你可以先从使用标准的 Espresso ViewMatchers 和 ViewAssertions - 语句断言是否显示正确的信息开始: +要为这两种情况编写测试,你可以先从使用标准的 Espresso ViewMatchers 和 ViewAssertions 语句断言是否显示正确的信息开始: ``` @Test @@ -169,8 +168,7 @@ public void clickMovieItemView() { 到这里就完成了,希望这些知识对你有用。 -在接下来的文章里,我会介绍如何使用 Espresso 测试视图时支持 TalkBack 服务(译者注:Talkback - 是一款由谷歌官方开发的系统工具软件,它的定位是帮助盲人或者有视力障碍的用户提供语言辅助)。 +在接下来的文章里,我会介绍如何使用 Espresso 测试视图时支持 TalkBack 服务(译者注:Talkback 是一款由谷歌官方开发的系统工具软件,它的定位是帮助盲人或者有视力障碍的用户提供语言辅助)。 --- From 094f4b1b63291d80b4b00bbc09addcb858e01b0d Mon Sep 17 00:00:00 2001 From: zhuzi Date: Sat, 29 Apr 2017 17:30:07 +0800 Subject: [PATCH 294/638] translation done --- ...t-the-future-of-component-based-styling.md | 128 ++++++++---------- 1 file changed, 58 insertions(+), 70 deletions(-) diff --git a/TODO/css-in-javascript-the-future-of-component-based-styling.md b/TODO/css-in-javascript-the-future-of-component-based-styling.md index ef3c032d366..595ab2ded82 100644 --- a/TODO/css-in-javascript-the-future-of-component-based-styling.md +++ b/TODO/css-in-javascript-the-future-of-component-based-styling.md @@ -10,27 +10,17 @@ 图片所属 [@jonathanzwhite](https://twitter.com/JonathanZWhite) -> By adopting inline styles, we can get all of the programmatic affordances of JavaScript.This gives us the benefits of something like a CSS pre-processor (variables, mixins, and functions). It also solves a lot of the problems that CSS has, such as global namespacing and styling conflicts. +使用行内样式使我们可以获得 JavaScript 的所有编程支持。这让我们获得类似 CSS 预处理器(变量、混入和函数)的好处,它也解决了 CSS 的很多问题,如全局命名空间和样式冲突。 -使用行内样式使我们可以获得 JavaScript 的所有编程支持。这给了我们类似 CSS 预处理器(变量、混入和函数)的好处。它也解决了 CSS 的很多问题,如全局命名空间和样式冲突。 +如果想要更深入了解 JavaScript 中的 CSS 所解决的问题,可以查看著名的演示幻灯:[React:JS 中的 CSS](https://speakerdeck.com/vjeux/react-css-in-js)。有关使用 Aphrodite 性能优化的案例研究,你可以阅读 [行内 CSS 在可汗学院:Aphrodite](http://engineering.khanacademy.org/posts/aphrodite-inline-css.htm)。如果你想了解更多,可以阅读 [Airbnb 的风格指南](https://github.com/airbnb/javascript/tree/master/css-in-javascript)。 -> For a deep dive into the problems that CSS in JavaScript solves, check out the famous presentation: [React CSS in JS](https://speakerdeck.com/vjeux/react-css-in-js). For a case study on the performance improvements you get from Aphrodite, you can read [Inline CSS at Khan Academy: Aphrodite](http://engineering.khanacademy.org/posts/aphrodite-inline-css.htm). If you want to learn more about CSS in JavaScript best practices, check out [Airbnb’s styleguide](https://github.com/airbnb/javascript/tree/master/css-in-javascript). - -如果想要更深入了解 JavaScript 中的 CSS 所解决的问题,可以查看著名的演示幻灯:[React CSS in JS](https://speakerdeck.com/vjeux/react-css-in-js)。有关使用 Aphrodite 性能优化的案例研究,你可以阅读 [行内 CSS 在可汗学院:Aphrodite](http://engineering.khanacademy.org/posts/aphrodite-inline-css.htm)。如果你想了解更多,可以阅读 [Airbnb 的风格指南](https://github.com/airbnb/javascript/tree/master/css-in-javascript). - -> In addition we’ll be using inline JavaScript styles to build components to address some of the fundamentals of design I covered in one of my previous articles: [Before you can master design, you must first master the fundamentals](https://medium.freecodecamp.com/before-you-can-master-design-you-must-first-master-the-fundamentals-1981a2af1fda). - -此外,我们将使用行内 JavaScript 样式来构建组件,以解决我之前的一篇文章([掌握设计之前,必须掌握基本原理](https://medium.freecodecamp.com/before-you-can-master-design-you-must-first-master-the-fundamentals-1981a2af1fda))中涉及的一些设计基础问题。 +此外,我们将使用行内 JavaScript 样式来构建组件,以解决我之前的一篇文章([掌握设计之前,必须掌握基本原理](https://medium.freecodecamp.com/before-you-can-master-design-you-must-first-master-the-fundamentals-1981a2af1fda))中涉及的一些基础设计问题。 ### 一个启发性的例子 ### -> Let’s start off with a simple example: creating and styling a button. - -让我们从一个简单的例子开始,构建一个按钮并给它添加样式。 +让我们从一个简单的例子开始:构建一个按钮并给它添加样式。 -> Normally the component and its associated styles would go in the same file: `Button` and `ButtonStyles`. This is because they fall under the same concern: the view. However, for this example, I broke up the code into multiple gists to make it more digestible. - -一般来说,组件及其样式在同一个文件中:`Button` 和 `ButtonStyles`。这是因为他们都属于视图层。但是,下面的例子中,我将代码拆分成多个 gist,以便更容易理解。 +一般来说,组件及其样式在同一个文件中:`Button` 和 `ButtonStyles`。这是因为他们都属于视图层。但是,下面的例子中,我将代码拆分成多个代码片段,以便更容易理解。 下面就是按钮组件: @@ -48,9 +38,7 @@ function Button(props) { } ``` -> This is nothing unexpected — just a stateless React component. Where Aphrodite comes into play is in the `className` property. The function `css` takes in a `styles` object and converts it into css. The `styles` object is created with Aphrodite’s `StyleSheet.create({ ... })` function. You can see the output of `StyleSheet.create({ ... })` with this [Aphrodite playground](https://output.jsbin.com/qoseye?). - -这没什么特别的,只是一个无状态的 React 组件。Aphrodite 起作用的地方是在 `className` 属性中。`css` 函数接受一个 `styles` 对象为参数并将其转换为 `css`。`styles` 对象是由 Aphrodite 的函数 `StyleSheet.create({ ... })` 创建的,你可以用 [Aphrodite playground](https://output.jsbin.com/qoseye?) 来看下这个函数的输出结果。 +它没什么特别的,只是一个无状态的 React 组件。Aphrodite 起作用的地方是在 `className` 属性中。`css` 函数接受一个 `styles` 对象为参数并将其转换为 `css`。`styles` 对象是由 Aphrodite 的函数 `StyleSheet.create({ ... })` 创建的,你可以用 [Aphrodite playground](https://output.jsbin.com/qoseye?) 来查看这个函数的输出结果。 **下面是按钮的样式表:** @@ -73,27 +61,23 @@ const styles = StyleSheet.create({ }); ``` -> One of the benefits of Aphrodite is that migration is straightforward and the learning curve is low. Properties like `border-radius` become `borderRadius` and values become strings. Pseudo-selectors, media queries, and font definitions all work. In addition, vendor prefixes are added automatically. - -Aphrodite 的优势之一就是迁移是很直观的,学习曲线很平缓。类似 `border-radius` 变成 `borderRadius`,值变成字符串。伪类选择器、媒体查询、字体定义都可以正常工作。另外也自动添加浏览器引擎前缀。 +Aphrodite 的优势之一是迁移很直观,学习曲线较平缓。类似 `border-radius` 变成 `borderRadius`,值变成字符串,伪类选择器、媒体查询、字体定义都可以正常工作。另外也可以自动添加浏览器引擎前缀。 **下面就是按钮的样子:** ![](https://cdn-images-1.medium.com/max/800/1*x1ccRv9UGvcxBvz4TvC4Qg.png) -> With this example in mind, **let’s take a look at how we can use Aphrodite to build a basic visual design system**, focusing on the following design fundamentals: typography and spacing. - -以这个例子为基础,**让我们来看看我们如何使用 Aphrodite 来构建一个基本的视觉设计系统**,着重关注字体排版和间距两个设计基础元素。 +以这个例子为基础,**让我们来看看如何使用 Aphrodite 来构建一个基本的视觉设计系统**,着重关注排版和间距两个设计基础元素。 -### Fundamental №1 —Typography ### +### 设计基础第一部分:排版 ### -Let’s start off with typography, a fundamental basis for design. **The first step is to define typography constants**. And unlike Sass or Less, constants for Aphrodite can go in a JavaScript or JSON file. +我们先从排版开始,这是设计基础要素。**第一步是定义排版常数**。与 Sass 及 Less 不一样,Aphrodite 的常数可以直接放在 JavaScript 中或 JSON 文件中。 -#### Define typography constants +#### 定义排版常数 -When creating constants, **use semantic names for your variables**. For example, instead of naming one of your font-sizes `h2`, use a name like `displayLarge` that describes its *role*. Similarly, for font-weights, instead of naming one of your weights `600`, give it a name like `semibold` to describe its *effect*. +在定义常量时,**使用语义化的变量名**。例如,在给字体大小命名时,不要使用 `h2`,使用 `displayLarge` 描述它的作用。类似的,不要给字体权重命名 `600`,使用 `semibold` 描述它的效果。 -``` +```javascript export const fontSize = { // heading displayLarge: '32px', @@ -136,21 +120,21 @@ export const lineHeight = { }; ``` -It’s important to get the values right for variables like font-sizes and line-heights. This is because they directly affect the vertical rhythm within a design. Vertical rhythm is a concept that helps you achieve consistent spacing between elements. +设置正确的字体大小和行高变量的值是很重要的。这是因为他们直接影响了设计的垂直规律。垂直规律是一个能帮助你实现一致的元素间距的概念。 -For more on vertical rhythm, you can read this article: [Why is Vertical Rhythm an Important Typography Practice?](https://zellwk.com/blog/why-vertical-rhythms/) +想要了解更多有关垂直规律的内容,你可以阅读这篇文章:[为什么垂直规律对排版实践很重要?](https://zellwk.com/blog/why-vertical-rhythms/) ![](https://cdn-images-1.medium.com/max/800/1*Ehj9XMvQ9wJNhxWNqwXfKw.png) -[Use a calculator to determine line-heights](https://drewish.com/tools/vertical-rhythm/) +[上图:行高计算器](https://drewish.com/tools/vertical-rhythm/) -There is a science behind choosing the values for your line-heights and font-sizes. We can use mathematic ratios to generate a set of potential sizes candidates. A few weeks ago, I wrote an article detailing the methodology: [Typography can make or break your design: a process for choosing type](https://medium.freecodecamp.com/typography-can-make-your-design-or-break-it-7be710aadcfe). For determining font-sizes, you use [Modular Scale](http://www.modularscale.com/). For determining line-heights, you can use this [vertical rhythm calculator](https://drewish.com/tools/vertical-rhythm/). +选择行高以及字体大小的背后是有科学原理的。我们可以使用比率生成一组可能的值。几周前,我写了一篇文章,详细地介绍了方法细节([排版可以成就设计,也可以毁了设计](https://medium.freecodecamp.com/typography-can-make-your-design-or-break-it-7be710aadcfe))。你可以使用 [Modular Scale](http://www.modularscale.com/) 确定字体大小,使用 [vertical rhythm calculator](https://drewish.com/tools/vertical-rhythm/) 计算行高。 -#### Define a heading component #### +#### 定义标题组件 #### -After defining our typography constants, the next step is to create a component to consume the values. **The goal of the component is to enforce consistency in design and implementation for headings across the codebase.** +定义好了排版常量后,下一步就是使用它们创建一个组件。**这个组件的目标是对整个代码库中的标题实现一致的设计**。 -``` +```javascript import React, { PropTypes } from 'react'; import { StyleSheet, css } from 'aphrodite/no-important'; import { tagMapping, fontSize, fontWeight, lineHeight } from '../styles/base/typography'; @@ -191,9 +175,9 @@ export const styles = StyleSheet.create({ }); ``` -The `Heading` component is a stateless function that takes in a tag as a property and returns the tag with its associated style. This is possible because we defined the tag mappings earlier in the constants file. +`Heading` 组件是一个无状态的函数,接收一个标签作为属性,并返回这个标签连带它的样式。我们在前面的常量中定义了标签映射,所以这是可行的。 -``` +```javascript ... export const tagMapping = { h1: 'displayLarge', @@ -204,9 +188,9 @@ export const tagMapping = { }; ``` -At the bottom of the component file, we define our `styles` object. This is where we use the typography constants. +在组件文件的下方我们定义了 `styles` 对象,我们就是在此处使用排版常量的。 -``` +```javascript export const styles = StyleSheet.create({ displayLarge: { fontSize: fontSize.displayLarge, @@ -218,9 +202,9 @@ export const styles = StyleSheet.create({ }); ``` -And this is how the `Heading` component would be used: +`Heading` 组件是这样调用的: -``` +```javascript function Parent() { return ( Hello World @@ -228,17 +212,19 @@ function Parent() { } ``` -With this approach, **we reduce unexpected variability in our type system**. We avoid the pitfall of a hundred different font sizes by removing the need for global styles and standardizing headings across the codebase. In addition, this approach we took to building the `Heading` component can be applied to building a `Text` component for body copy. +通过这种方法,**我们可以减少类型的意外变化**。通过取消全局样式以及标准化标题,我们避免了上百种字体大小的问题。此外,这种方法还可以应用于构建 `Text` 组件。 -### Fundamental №2 — Spacing ### +### 设计基础第二部分:间距 ### -**Spacing controls both vertical and horizontal rhythm in design**. That makes spacing pivotal to establishing a visual design system. Just like in the typography section, the first step to address spacing is to define spacing constants. +**间距同时控制着设计中的垂直与水平规律**。所以间距对建立视觉设计系统至关重要。和排版部分一样,第一步也是设定间距常量。 -#### Define spacing constants ### +#### 定义间距常量 ### -When defining spacing constants for the margins between elements, we can adopt a mathematic approach. Using a `spacingFactor` constant, we can generate a set of distances based on a common factor. **This approach ensures that we have logical and consistent spacing between elements.** +> When defining spacing constants for the margins between elements, we can adopt a mathematic approach. Using a `spacingFactor` constant, we can generate a set of distances based on a common factor. **This approach ensures that we have logical and consistent spacing between elements.** -``` +当为元素之间的 margin 定义间距常量时,我们可以采取一种数学方法。使用一个 `spacingFactor` 常量来生成一组距离。**这种方法确保元素之间的间距是有逻辑并且一致的**。 + +```javascript const spacingFactor = 8; export const spacing = { space0: `${spacingFactor / 2}px`, // 4 @@ -255,7 +241,9 @@ export const spacing = { }; ``` -The example above uses a linear scale, one to thirteen. However, experiment with different scales and ratios. Designs require different scales based on their purpose, their audience, and the devices they target. As an example,** here are the first six computed distances using the golden ratio** with a `spacingFactor` of eight. +> The example above uses a linear scale, one to thirteen. However, experiment with different scales and ratios. Designs require different scales based on their purpose, their audience, and the devices they target. As an example,**here are the first six computed distances using the golden ratio** with a `spacingFactor` of eight. + +上面的例子采用了线性关系,从 1 到 13。不管怎样,多试验几种不同的尺度和比例的搭配才能找到合适的方案。目的、受众、目标设备的不同都需要在设计时考虑。**下面是使用黄金比率计算出来的前 6 个距离**,以 8 个 `spacingFactor` 为例。 Golden Ratio (1:1.618) @@ -266,9 +254,11 @@ The example above uses a linear scale, one to thirteen. However, experiment with 8.0 x (1.618 ^ 4) = 54.82 8.0 x (1.618 ^ 5) = 88.71 -This is what the spacing scale would look like in code. I added a helper function to handle the computation and round off the output to its nearest pixel value. +> This is what the spacing scale would look like in code. I added a helper function to handle the computation and round off the output to its nearest pixel value. -``` +下面是在代码中如何写间距比例。我添加了一个帮助处理间距计算结果的函数,它会返回其最近的像素值。 + +```javascript const spacingFactor = 8; export const spacing = { space0: `${computeGoldenRatio(spacingFactor, 0)}px`, // 8 @@ -284,11 +274,11 @@ function computeGoldenRatio(spacingFactor, exp) { } ``` -After defining our spacing constants, we can use them to add margins to elements in our design. **One approach we can take is to import the spacing constants and consume them in components**. +定义好常量后,我们就可以用它们给元素添加间距。**一种方法就是在组件中 import**。 -For example, let’s add a `marginBottom` to the `Button` component. +例如,下面我们给 `Button` 组件添加 `marginBottom`。 -``` +```javascript import { spacing } from '../styles/base/spacing'; ... @@ -301,11 +291,11 @@ const styles = StyleSheet.create({ }); ``` -This works in most scenarios. However, what happens if we want to change the `marginBottom` property of the button based on where the button is place? +多数情况下这都是有效的。但是如果我们想要根据按钮的位置来修改它的 `marginBottom` 属性呢? -One way to achieve variable margins is to override the margin style from the consuming parent component. An alternative approach is to **create a **`Spacing`**component to control the vertical margins on elements**. +实现可变边距的一种方法是覆盖从父组件继承的样式。另一种方法是**创建一个 **`Spacing` **组件来控制元素的垂直边距**。 -``` +```javascript import React, { PropTypes } from 'react'; import { spacing } from '../../base/spacing'; @@ -324,15 +314,15 @@ function Spacing(props) { export default Spacing; ``` -Using this approach, we can remove the responsibility of setting margins out of the child component and into the parent component. **This way, the child component becomes layout agnostic, not requiring any knowledge of where to place itself in relation to other elements.** +这种方法可以将设置边距的任务从子组件转移到父组件上。**这样,子组件变成了对布局无感知的组件,它不需要知道自己将被放置在何处以及与其他元素的关联**。 -This works because components like buttons, inputs, and cards may need variable margins. For example, a button in a form may need larger margins than a button in a navigation bar. The caveat is that if a component always has consistent margins, then it would make more sense to handle margins within the component. +由于按钮、输入框、卡片等组件可能需要可变的间距,所以这种方法是有效的。例如,表单中的按钮可能比导航栏的按钮需要更大的边距。需要注意的是,如果一个组件始终具有一致的边距,那么在组件内部处理边距更好。 -Also you may have noticed that the examples only use `marginBottom`. This is because **defining all your vertical margins in one direction allows you avoid collapsing margins and keep track of the vertical rhythm of your design**. You can read more on this in Harry Robert’s article, [Single-direction margin declarations](https://csswizardry.com/2012/06/single-direction-margin-declarations/). +你可能注意到前面的例子中只使用了 `marginBottom` ,这是因为**在一个方向定义所有的垂直边距可以避免边距合并,并能跟踪垂直规律**。你可以从 Harry Robert 的文章 [单向边距声明](https://csswizardry.com/2012/06/single-direction-margin-declarations/) 中了解更多这方面知识。 -On a final note, you can also use the spacing constants you defined as padding. +最后,你还可以使用间距常量来定义 padding。 -``` +```javascript import React, { PropTypes } from 'react'; import { StyleSheet, css } from 'aphrodite/no-important'; import { spacing } from '../../styles/base/spacing'; @@ -358,21 +348,19 @@ export const styles = StyleSheet.create({ }); ``` -By using using the same spacing constants for both margins and padding, you can achieve more visual consistency in your design. +对 margin 和 padding 使用相同的间距常量,可以在设计中实现更好的视觉一致性。 -Here’s what the result might look like: +结果可能大致如下: ![](https://cdn-images-1.medium.com/max/800/1*oDkbVmgCJ4ss5fuRNvzoUg.png) -[By using spacing constants for your margins and padding, you can achieve more visual consistency.](https://twitter.com/JonathanZWhite) - -Now that you have a grasp of CSS in JavaScript, go out and experiment. Try incorporating inline JavaScript styles into your next project. I think **you’ll appreciate being able to work in a single context to handle all of your styling and view concerns**. +现在你已经大致了解了 JavaScript 中的 CSS 了,去试验一下吧。尝试在下个项目中采用行内 JavaScript 样式吧。我想**你会喜欢上能够在同一个上下文中处理所有样式及视图问题的感觉**。 -On the topic of CSS and JavaScript, what are some new developments that you’re excited about? Personally I’m excited about async/await. Leave me a note or send me a [tweet](https://twitter.com/jonathanzwhite) on Twitter. +有关 CSS 和 JavaScript 的主题中,你对什么新的发展感兴趣呢?我个人对 async/await 非常感兴趣。给我留言或者在 [Twitter](https://twitter.com/jonathanzwhite) 上发信息给我吧。 -You can find me on Medium where I publish every week. Or you can follow me on [Twitter](https://twitter.com/JonathanZWhite), where I post non-sensical ramblings about design, front-end development, and virtual reality. +你可以在 Medium 上找到我,我每周都会发布一篇文章。你也可以在 [Twitter](https://twitter.com/jonathanzwhite) 上关注我,我会在那里发布一些有关设计、前端开发和虚拟现实的随笔。 -*P.S. If you enjoyed this article, it would mean a lot if you clicked the 💚 and shared with friends.* +*如果你喜欢这篇文章,欢迎给我点赞 ❤ 并分享给朋友,非常感谢!* [![](https://cdn-images-1.medium.com/max/600/1*mxQhZLqG7l5dMLvxYAklgw.png)](http://mrwhite.space/signup) From a0972cad285c4abf6ecc79f1751cf72f390ef4ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=B9=E5=8F=B7=E4=B8=89?= Date: Sat, 29 Apr 2017 18:34:29 +0800 Subject: [PATCH 295/638] Update code-comments-the-good-the-bad-and-the-ugly.md --- TODO/code-comments-the-good-the-bad-and-the-ugly.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO/code-comments-the-good-the-bad-and-the-ugly.md b/TODO/code-comments-the-good-the-bad-and-the-ugly.md index 9924620abc2..38d02db3bbd 100644 --- a/TODO/code-comments-the-good-the-bad-and-the-ugly.md +++ b/TODO/code-comments-the-good-the-bad-and-the-ugly.md @@ -1,5 +1,5 @@ > * 原文地址:[Putting comments in code: the good, the bad, and the ugly.](https://medium.freecodecamp.com/code-comments-the-good-the-bad-and-the-ugly-be9cc65fbf83) -> * 原文作者:[Bill Sourour](https://medium.freecodecamp.com/@BillSourour?source=post_header_lockup) +> * 原文作者:本文已获原作者 [Bill Sourour](https://medium.freecodecamp.com/@BillSourour) 授权 > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 译者: [bambooom](https://github.com/bambooom) > * 校对者:[zhangqippp](https://github.com/zhangqippp)、[steinliber](https://github.com/steinliber) From f25b6f439f7951921f2782deaa45af404876a46e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ARX-8=5C=E6=AC=A7=E9=98=B3=E5=A4=8F=E6=98=B1?= Date: Sun, 30 Apr 2017 10:55:53 +0800 Subject: [PATCH 296/638] =?UTF-8?q?2017.4.30=20=E5=88=9D=E7=BF=BB=E5=AE=8C?= =?UTF-8?q?=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO/project-need-react.md | 121 +++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/TODO/project-need-react.md b/TODO/project-need-react.md index f861a660328..f451a421379 100644 --- a/TODO/project-need-react.md +++ b/TODO/project-need-react.md @@ -6,114 +6,235 @@ # When Does a Project Need React? # +# 项目什么时候需要 React 框架呢? # + You know when a project needs HTML and CSS, because it's all of them. When you reach for JavaScript is fairly clear: when you need interactivity or some functionality that only JavaScript can provide. It used to be fairly clear when we reached for libraries. We reached for jQuery to help us simplify working with the DOM, Ajax, and handle cross-browser issues with JavaScript. We reached for underscore to give us helper functions that the JavaScript alone didn't have. +你知道什么时候项目需要 HTML 和 CSS,因为这是项目的基础。什么时候用 JavaScript 也很清楚:当你需要只有它能提供的交互功能的时候。过去我们什么时候应该用代码库也很清楚:我们需要 jQuery 来帮助我们简化 DOM 操作,调用 Ajax,处理浏览器兼容问题;我们需要 Underscore 提供 JavaScript 无法单独提供的函数式编程。 + As the need for these libraries fades, and we see a massive rise in new frameworks, I'd argue it's not as clear **when to reach for them**. At what point do we need React? +但是随着对这些代码库的需求逐渐消失,我们看到很多新兴的框架的大幅增长。我认为就不那么容易确定**何时需要它们**了。比如说,我们什么情况下需要 React 框架? + I'm just going to use React as a placeholder here for kinda large JavaScript framework thingies. Vue, Ember, Svelte... whatever. I understand they aren't all the same, but when to reach for them I find equally nebulous. +在众多的 JavaScript 框架中 —— Vue、Ember、Svelte ... 不管哪一个,我想以 React 框架为例子来探讨它适合什么项目。我明白这些框架并不完全相同,但是使用它们的时机应该是有一些共性的。 + Here's my take. +这是我的做法。 + ### ✅ Because there is lots of state. ### +### ✅ 当项目中有大量的状态的时候 ### + Even "state" is a bit of a nebulous word. Imagine things like this: +即便“状态(state)”这个词也无法完全准确的表达我的意思。想象一下这些情况: + - Which navigation item is active +- 导航栏的哪个栏目正处于激活状态 - Whether a button is disabled or not +- 一个按钮是否被禁用 - The value of an input +- 输入框的值 - Which accordion sections are expanded +- 哪一个下拉框是弹出的状态 - When an area is loading +- 何时加载某个区域 - The user that is logged in and the team they belong to +- 登陆的用户如何进行权限控制 - Whether the thing the user is working on is published, or a draft +- 用户编写的文章是已发布状态还是草稿状态 "Business logic"-type stuff that we regularly deal with. State can also be straight up content: +“业务逻辑” - 我们经常处理的这类东西。状态也可能和内容直接相关: + - All the comments on an article and the bits and bobs that make them up +- 一篇文章中所有的评论,以及零零碎碎的组成它们的东西。 - The currently viewed article and all its metadata +- 当前正在查看的文章,以及该文章的一些属性 - An array of related articles and the metadata for those +- 一系列相关的文章,以及这些文章的属性 - A list of authors +- 一份作者列表 - An an activity log of recent actions a user has taken +- 一份记录用户近期操作的活动日志 React doesn't help you *organize* that state, it just says: I know you need to *deal* with state, so let's just *call* it state and have programmatic ways to set and get that state. +React 框架并没有帮助你**组织**这些状态,它只是说:我知道你需要处理状态的问题,所以我们不如把它设为 state 属性,通过编程的方式进行读写。 + Before React, we might have *thought* in terms of state, but, for the most part, didn't manage it as a direct concept. +在有 React 框架之前,我们也许**考虑过**状态的定义,但是大部分时候并没有把它当作一个直接的概念去管理。 + Perhaps you've heard the phrase "single source of truth"? A lot of times we treated the DOM as our single source of truth. For example, say you need to know if a form on your website is able to be submitted. Maybe you'd check to see if `$(".form input[type='submit']).is(":disabled")` because all your business logic that dealt with whether or not the form could be submitted or not ultimately changed the disabled attribute of that button. So the button became this defacto source of truth for the state of your app. +也许你听说过这个短语“单一数据源”?很多时候我们把 DOM 作为我们的单一数据源。比如说,你需要知道是否可以提交某个表单了。也许你会用 `$(".form input[type='submit']).is(":disabled")` 去检查一下,因为所有影响表单是否可提交的业务逻辑最终都会改变按钮的 disable 属性。所以按钮变成了你的 app 事实上的数据源。 + Or say you needed to figure of the name of the first comment author on an article. Maybe you'd write `$(".comments > ul > li:first > h3.comment-author).text()` because the DOM is the only place that knows that information. +或者说,你需要知道某篇文章的第一个评论者的名字,也许你会这样写 `$(".comments > ul > li:first > h3.comment-author).text()`,因为 DOM 是你唯一可以获得这些信息的地方。 + React kinda tells us: +React 框架这样告诉我们: + 1. Let's start thinking about all that stuff as state. + + 我们把这些所有的东西都想像成状态(state)。 + 2. I'll do ya one better: state is a chunk of JSON, so it's easy to work with and probably works nicely with your back end. + + 我会为你做好一件事:把状态转换为一串 JSON 字符串,这样的话处理起来很容易,也许你的服务端可以处理的很漂亮。 + 3. And one more even better: You build your HTML using bits of that state, and you won't have to deal with the DOM directly at all, I'll handle all that for you (and likely do a better/faster job than you would have.) + 另外还有一个更好的地方是:你可以用这些状态(state)直接构建 HTML ,你根本不需要直接操作 DOM,我都替你处理了(也许比你亲自处理的要更快更好)。 + ### ✅ To Fight Spaghetti. ### +### ✅ 对抗面条式代码 (Spaghetti)### + This is highly related to the state stuff we were just talking about. +这和我们刚才讨论过的状态非常有关系。 + "Spaghetti" code is when code organization and structure has gotten away from you. Imagine, again, a form on your site. It has some business logic stuff that specifically deals with the inputs inside of it. Perhaps there is a number input that, when changed, display the result of some calculation beside it. The form can also be submitted and needs to be validated, so perhaps that code is in a validation library elsewhere. Perhaps you disable the form until you're sure all JavaScript has loaded elsewhere, and that logic is elsewhere. Perhaps when the form is submitted, you get data back and that needs logic and handling. Nothing terribly surprising here, but you can see how this can get confusing quickly. How does a new dev on the project, looking at that form, reason out everything that is going on? +“面条式”代码,指的是代码的组织结构已经脱离你的掌控。再想象一下,假设有这么一个表单,它有一些专门处理表单内输入框的业务逻辑。该表单内有这么一个数字输入框,当这个输入框的值改变的时候,在旁边显示根据该值进行某些计算后的结果。这个表单可以被提交至服务端,因此也需要合法性检查,而也许合法性检查的代码位于其他地方的验证库中。也许在确定某处的 JavaScript 代码全部加载完之前,你还需要禁用此表单,而这个逻辑也在别的地方。也许当表单提交后,你还需要处理一些返回值。没有什么特别让人意外的功能,但是凑在一起就很容易让人蒙圈。如果这个项目由一个新的开发人员接手后,当他看到这个表单时他如何能捋清这些逻辑呢? + React encourages the use of building things into modules. So this form would likely either be a module of its own or comprised of other smaller modules. Each of them would handle the logic that is directly relevant to it. +React 框架鼓励把东西打包成组件。所以这个表单要么自己是一个组件,要么由其他的小组件组成。每一个组件只处理与自己直接相关的逻辑。 + React says: *well, you aren't going to be watching the DOM directly for changes and stuff, because the DOM is mine and you don't get to work with it directly*. Why don't you start thinking of these things as part of the state, change state when you need to, and I'll deal with the rest, rerendering what needs to be rerendered. +React 框架说:**嗯,你不会直接看到 DOM 的变化,因为 DOM 是我的,你无法直接操作它**。为什么你不把这些东西想象成状态的一部分,当需要的时候就改变状态(state)。我会处理其他的事情,重新渲染需要被渲染的界面。 + It should be said that React itself doesn't entirely solve spaghetti. You can still have state in all kinds of weird places, name things badly, and connect things in weird ways. +应该说,只有 React 框架还不足以解决面条式代码。因为状态也可能出现在各个奇怪的地方,或者状态起的名字很糟糕,或者用莫名其妙的方式调用。 + In my limited experience, it's Redux that is the thing that really kills spaghetti. Redux says: I'll handle *all* the important state, totally globally, not module-by-module. I am the absolute source of truth. If you need to change state, there is quite a *ceremony* involved (I've heard it called that, and I like it.) There are reducers and dispatched actions and such. All changes follow the ceremony. +在我有限的经验内,Redux 框架才能真正解决面条式代码的问题。Redux 框架说:我会处理**所有**重要的状态,都是全局的,不是组件依赖的。我才是唯一的数据源。如果你需要改变状态,就要采用特定的**仪式**(我听说它是这么叫的,而且我喜欢这么叫)。通过 reducers 和被分发的(dispatched) actions,所有的改变都会遵循这种仪式。 + If you go the Redux road (and there are variations of it, of course), you end up with really solid code. It's much harder to break things and there are clear trails to follow for how everything is wired together. +如果你准备在项目中加入 Redux(当然也需要一点微小的改变),那么你就可以和硬编码说再见了。通过加入 Redux 框架,组件会变的高内聚,也很容易理清整个需求的逻辑走向了。 + ### ✅ Lots of DOM management. ### +### ✅ 要控制大量的DOM ### + Manually handling the DOM is probably the biggest cause of spaghetti code. +手动处理 DOM 可能是引起面条式代码的最大原因。 + 1. Inject HTML over here! + + 在这里插入一段 HTML ! + 2. Rip something out over here! + + 在这里把某些东西扔出去! + 3. Watch this area for this event! + + 监听特定区域的特定事件(event)! + 4. Bind a new event over here! + + 在这里绑定一个新事件! + 5. New incoming content! Inject again! Make sure it has the right event bindings! + 又来了新内容。再次插入到 HTML 里,确保它绑定了正确的事件! + + All these things can happen any time from anywhere in an app that's gone spaghetti. Real organization has been given up and it's back to the DOM as the source of truth. It's hard to know exactly what's going on for any given element, so everybody just asks the DOM, does what they need to do, and crosses their fingers it doesn't mess with somebody else. +此类事情可以发生在一个 app 的任何地方、任何时间,这就造成了面条式代码。手动管理是不靠谱的,因为这么做的话又变成 DOM 数据源了。很难准确的知道任何给定的元素发生了什么,所以每个人只好直接查询 DOM ,做他们必须做的事情,顺便向上帝祈祷他们这么做没干扰到别人。 + React says: you don't get to deal with the DOM directly. I have a virtual DOM and I deal with that. Events are bound directly to the elements, and if you need it to do something above and beyond something directly handle-able in this module, you can kind of ceremoniously call things in higher order modules, but that way, the breadcrumb trail can be followed. +React 框架说:你不需要直接操作 DOM 。我用虚拟 DOM 来处理真实的 DOM。事件直接绑定在组件内,如果你想在该组件之外做些什么的话,只需要按照规范在父组件里调用就可以了。通过这种方式,所有的逻辑就有迹可循了。 + *Complicated* DOM management is another thing. Imagine a chat app. New chat messages might appear because a realtime database has new data from other chatters and some new messages have arrives. Or you've typed a new message yourself! Or the page is loading for the first time and old messages are being pulled from a local data store so you have something to see right away. Here's [a Twitter thread](https://twitter.com/mjackson/status/849636985740210177) that drives that home. +管理**复杂的** DOM 是另一件 适合 React 框架的事情。想象有一个聊天软件,当数据库接收到其他聊天者传递来的新聊天信息时,在聊天窗口里应该显示这些新的信息。否则你只能自己给自己聊天了!或者当聊天页面第一次被加载的时候,可以从本地数据库里找出几条旧信息显示出来,这样你立刻有东西可以看了。比如说这个[推特例子](https://twitter.com/mjackson/status/849636985740210177)。 + ### ❌ Just because. It's the new hotness. ### +### ❌ 只是因为,React 框架是目前最火的框架。 ### + Learning something for the sake of learning something is awesome. Do that. +为了学习而学习,是非常酷的。坚持下去。 + Building a project for clients and real human being users requires more careful consideration. +为了满足用户的需求而构建项目则需要更谨慎一点。 + A blog, for example, *probably* has none of the problems and fits none of the scenarios that would make React a good fit. And because it's not a good fit, it's probably a *bad* fit, because it introduces complicated technology and dependencies for something that doesn't call for it. +举个例子,一个博客**也许**没什么复杂的逻辑,一点也不符合应该使用 React 框架的情况。所以如果不是很适合的话,那么也许就是**很不**适合 React 框架。因为这么做引入了复杂的技术,依赖了很多根本没用到的东西。 + And yet, gray area. If that blog is a SPA ("Single Page App", e.g. no browser refreshing) that is built from data from a headless CMS and had fancy server-side rendering... well maybe that is React territory again. +在完全适合和完全不适合之间,如果这个博客是一个 SPA (“单页面应用”,不需要浏览器刷新),通过 headless CMS 获取数据构建该博客,并且具有出色的服务端渲染...好吧,也许又是 React 框架的领域。 + The web app CMS that makes that blog? Maybe a good choice for React, because of all the state. +网络 app 内容管理系统创建的这种博客?也许用 React 是一个好选择,因为也是一大堆的状态。 + ### ❌ I just like JavaScript and want to write everything in JavaScript. ### +### ❌ 我就是喜欢 JavaScript ,就是想用它来编写任何东西。 ### + People get told, heck, I've told people: learn JavaScript. It's huge. It powers all kinds of stuff. There are jobs in it. It's not going anyway. +我经常安利周围的人:学习 JavaScript。因为 JavaScript 的知识太丰富了。它能做很多很多的事情,所以好好学习 JavaScript 永远不会过时。 + It's only in recent web history that it's become possible to never leave JavaScript. You got Node.js on the server side. There are loads of projects that yank CSS out of the mix and handle styles through JavaScript. And with React, your HTML is in JavaScript too. +只有在最近的网络发展中,Javascript 才变的不可或缺。你通过 Node.js 构建服务端,也有很多项目可以通过 JavaScript 处理 CSS。现在通过 React 框架,你还可以通过 JavaScript 来操作 HTML。 + All JavaScript! All hail JavaScript! +万物归于 JavaScript!JavaScript 万岁! + That's cool and all, but again, just because you can doesn't mean you should. Not all projects call for this, and in fact, most probably don't. +JavaScript 确实碉堡了,但是你可以这么做并不意味着你必须这么做。并不是所有的项目都必须使用 JavaScript ,而且事实上,有相当一部分有可能压根不需要。 + ### ☯️ That's what I know. ### +### ☯️ 这就是我所知道的。 ### + (There are decent emojis for YES and NO, but MAYBE is tougher!) +(**是**和**否**有体面的表情,但是**也许**更坚韧!) + + You're learning. Awesome. Everybody is. Keep learning. The more you know the more informed decisions you can make about what tech to use. +你在学习,太好了。每个人都在学习,所以坚持学习吧。你知道的越多,你越知道该应该用什么技术。 + But sometimes you gotta build with what you know, so I ain't gonna ding ya for that. +但是很多时候你只能以现有的技术来构建项目,所以我也不会反复强调这一点。 + ### ☯️ That's where the jobs are. ### +### ☯️ 这就是工作。 ### + Not everybody has a direct say in what technology is used on any given project. Hopefully, over time, you have influence in that, but that takes time. Eden says she [spent 2 years with Ember](https://twitter.com/edenthecat/status/849640183360352257) because that's where the jobs were. No harm in that. Everybody's gotta get paid, and Ember might have been a perfect fit for those projects. +在给定的任何项目中,不是每个人都决定应该底用什么技术。希望随着时间的增长,你可以更大层度上的影响决策。Eden 说她[花了两年的时间研究 Ember](https://twitter.com/edenthecat/status/849640183360352257),因为这就是她的工作。没有任何冒犯的意思,但是拿人钱财就得替人消灾。Ember 也许比较适合这些项目。 --- From 7a4f3b6d814ef49cdc0b685db71666ec70201076 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ARX-8=5C=E6=AC=A7=E9=98=B3=E5=A4=8F=E6=98=B1?= Date: Sun, 30 Apr 2017 11:22:57 +0800 Subject: [PATCH 297/638] =?UTF-8?q?Revert=20"2017.4.30=20=E5=88=9D?= =?UTF-8?q?=E7=BF=BB=E5=AE=8C=E6=88=90"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit f25b6f439f7951921f2782deaa45af404876a46e. --- TODO/project-need-react.md | 121 ------------------------------------- 1 file changed, 121 deletions(-) diff --git a/TODO/project-need-react.md b/TODO/project-need-react.md index f451a421379..f861a660328 100644 --- a/TODO/project-need-react.md +++ b/TODO/project-need-react.md @@ -6,235 +6,114 @@ # When Does a Project Need React? # -# 项目什么时候需要 React 框架呢? # - You know when a project needs HTML and CSS, because it's all of them. When you reach for JavaScript is fairly clear: when you need interactivity or some functionality that only JavaScript can provide. It used to be fairly clear when we reached for libraries. We reached for jQuery to help us simplify working with the DOM, Ajax, and handle cross-browser issues with JavaScript. We reached for underscore to give us helper functions that the JavaScript alone didn't have. -你知道什么时候项目需要 HTML 和 CSS,因为这是项目的基础。什么时候用 JavaScript 也很清楚:当你需要只有它能提供的交互功能的时候。过去我们什么时候应该用代码库也很清楚:我们需要 jQuery 来帮助我们简化 DOM 操作,调用 Ajax,处理浏览器兼容问题;我们需要 Underscore 提供 JavaScript 无法单独提供的函数式编程。 - As the need for these libraries fades, and we see a massive rise in new frameworks, I'd argue it's not as clear **when to reach for them**. At what point do we need React? -但是随着对这些代码库的需求逐渐消失,我们看到很多新兴的框架的大幅增长。我认为就不那么容易确定**何时需要它们**了。比如说,我们什么情况下需要 React 框架? - I'm just going to use React as a placeholder here for kinda large JavaScript framework thingies. Vue, Ember, Svelte... whatever. I understand they aren't all the same, but when to reach for them I find equally nebulous. -在众多的 JavaScript 框架中 —— Vue、Ember、Svelte ... 不管哪一个,我想以 React 框架为例子来探讨它适合什么项目。我明白这些框架并不完全相同,但是使用它们的时机应该是有一些共性的。 - Here's my take. -这是我的做法。 - ### ✅ Because there is lots of state. ### -### ✅ 当项目中有大量的状态的时候 ### - Even "state" is a bit of a nebulous word. Imagine things like this: -即便“状态(state)”这个词也无法完全准确的表达我的意思。想象一下这些情况: - - Which navigation item is active -- 导航栏的哪个栏目正处于激活状态 - Whether a button is disabled or not -- 一个按钮是否被禁用 - The value of an input -- 输入框的值 - Which accordion sections are expanded -- 哪一个下拉框是弹出的状态 - When an area is loading -- 何时加载某个区域 - The user that is logged in and the team they belong to -- 登陆的用户如何进行权限控制 - Whether the thing the user is working on is published, or a draft -- 用户编写的文章是已发布状态还是草稿状态 "Business logic"-type stuff that we regularly deal with. State can also be straight up content: -“业务逻辑” - 我们经常处理的这类东西。状态也可能和内容直接相关: - - All the comments on an article and the bits and bobs that make them up -- 一篇文章中所有的评论,以及零零碎碎的组成它们的东西。 - The currently viewed article and all its metadata -- 当前正在查看的文章,以及该文章的一些属性 - An array of related articles and the metadata for those -- 一系列相关的文章,以及这些文章的属性 - A list of authors -- 一份作者列表 - An an activity log of recent actions a user has taken -- 一份记录用户近期操作的活动日志 React doesn't help you *organize* that state, it just says: I know you need to *deal* with state, so let's just *call* it state and have programmatic ways to set and get that state. -React 框架并没有帮助你**组织**这些状态,它只是说:我知道你需要处理状态的问题,所以我们不如把它设为 state 属性,通过编程的方式进行读写。 - Before React, we might have *thought* in terms of state, but, for the most part, didn't manage it as a direct concept. -在有 React 框架之前,我们也许**考虑过**状态的定义,但是大部分时候并没有把它当作一个直接的概念去管理。 - Perhaps you've heard the phrase "single source of truth"? A lot of times we treated the DOM as our single source of truth. For example, say you need to know if a form on your website is able to be submitted. Maybe you'd check to see if `$(".form input[type='submit']).is(":disabled")` because all your business logic that dealt with whether or not the form could be submitted or not ultimately changed the disabled attribute of that button. So the button became this defacto source of truth for the state of your app. -也许你听说过这个短语“单一数据源”?很多时候我们把 DOM 作为我们的单一数据源。比如说,你需要知道是否可以提交某个表单了。也许你会用 `$(".form input[type='submit']).is(":disabled")` 去检查一下,因为所有影响表单是否可提交的业务逻辑最终都会改变按钮的 disable 属性。所以按钮变成了你的 app 事实上的数据源。 - Or say you needed to figure of the name of the first comment author on an article. Maybe you'd write `$(".comments > ul > li:first > h3.comment-author).text()` because the DOM is the only place that knows that information. -或者说,你需要知道某篇文章的第一个评论者的名字,也许你会这样写 `$(".comments > ul > li:first > h3.comment-author).text()`,因为 DOM 是你唯一可以获得这些信息的地方。 - React kinda tells us: -React 框架这样告诉我们: - 1. Let's start thinking about all that stuff as state. - - 我们把这些所有的东西都想像成状态(state)。 - 2. I'll do ya one better: state is a chunk of JSON, so it's easy to work with and probably works nicely with your back end. - - 我会为你做好一件事:把状态转换为一串 JSON 字符串,这样的话处理起来很容易,也许你的服务端可以处理的很漂亮。 - 3. And one more even better: You build your HTML using bits of that state, and you won't have to deal with the DOM directly at all, I'll handle all that for you (and likely do a better/faster job than you would have.) - 另外还有一个更好的地方是:你可以用这些状态(state)直接构建 HTML ,你根本不需要直接操作 DOM,我都替你处理了(也许比你亲自处理的要更快更好)。 - ### ✅ To Fight Spaghetti. ### -### ✅ 对抗面条式代码 (Spaghetti)### - This is highly related to the state stuff we were just talking about. -这和我们刚才讨论过的状态非常有关系。 - "Spaghetti" code is when code organization and structure has gotten away from you. Imagine, again, a form on your site. It has some business logic stuff that specifically deals with the inputs inside of it. Perhaps there is a number input that, when changed, display the result of some calculation beside it. The form can also be submitted and needs to be validated, so perhaps that code is in a validation library elsewhere. Perhaps you disable the form until you're sure all JavaScript has loaded elsewhere, and that logic is elsewhere. Perhaps when the form is submitted, you get data back and that needs logic and handling. Nothing terribly surprising here, but you can see how this can get confusing quickly. How does a new dev on the project, looking at that form, reason out everything that is going on? -“面条式”代码,指的是代码的组织结构已经脱离你的掌控。再想象一下,假设有这么一个表单,它有一些专门处理表单内输入框的业务逻辑。该表单内有这么一个数字输入框,当这个输入框的值改变的时候,在旁边显示根据该值进行某些计算后的结果。这个表单可以被提交至服务端,因此也需要合法性检查,而也许合法性检查的代码位于其他地方的验证库中。也许在确定某处的 JavaScript 代码全部加载完之前,你还需要禁用此表单,而这个逻辑也在别的地方。也许当表单提交后,你还需要处理一些返回值。没有什么特别让人意外的功能,但是凑在一起就很容易让人蒙圈。如果这个项目由一个新的开发人员接手后,当他看到这个表单时他如何能捋清这些逻辑呢? - React encourages the use of building things into modules. So this form would likely either be a module of its own or comprised of other smaller modules. Each of them would handle the logic that is directly relevant to it. -React 框架鼓励把东西打包成组件。所以这个表单要么自己是一个组件,要么由其他的小组件组成。每一个组件只处理与自己直接相关的逻辑。 - React says: *well, you aren't going to be watching the DOM directly for changes and stuff, because the DOM is mine and you don't get to work with it directly*. Why don't you start thinking of these things as part of the state, change state when you need to, and I'll deal with the rest, rerendering what needs to be rerendered. -React 框架说:**嗯,你不会直接看到 DOM 的变化,因为 DOM 是我的,你无法直接操作它**。为什么你不把这些东西想象成状态的一部分,当需要的时候就改变状态(state)。我会处理其他的事情,重新渲染需要被渲染的界面。 - It should be said that React itself doesn't entirely solve spaghetti. You can still have state in all kinds of weird places, name things badly, and connect things in weird ways. -应该说,只有 React 框架还不足以解决面条式代码。因为状态也可能出现在各个奇怪的地方,或者状态起的名字很糟糕,或者用莫名其妙的方式调用。 - In my limited experience, it's Redux that is the thing that really kills spaghetti. Redux says: I'll handle *all* the important state, totally globally, not module-by-module. I am the absolute source of truth. If you need to change state, there is quite a *ceremony* involved (I've heard it called that, and I like it.) There are reducers and dispatched actions and such. All changes follow the ceremony. -在我有限的经验内,Redux 框架才能真正解决面条式代码的问题。Redux 框架说:我会处理**所有**重要的状态,都是全局的,不是组件依赖的。我才是唯一的数据源。如果你需要改变状态,就要采用特定的**仪式**(我听说它是这么叫的,而且我喜欢这么叫)。通过 reducers 和被分发的(dispatched) actions,所有的改变都会遵循这种仪式。 - If you go the Redux road (and there are variations of it, of course), you end up with really solid code. It's much harder to break things and there are clear trails to follow for how everything is wired together. -如果你准备在项目中加入 Redux(当然也需要一点微小的改变),那么你就可以和硬编码说再见了。通过加入 Redux 框架,组件会变的高内聚,也很容易理清整个需求的逻辑走向了。 - ### ✅ Lots of DOM management. ### -### ✅ 要控制大量的DOM ### - Manually handling the DOM is probably the biggest cause of spaghetti code. -手动处理 DOM 可能是引起面条式代码的最大原因。 - 1. Inject HTML over here! - - 在这里插入一段 HTML ! - 2. Rip something out over here! - - 在这里把某些东西扔出去! - 3. Watch this area for this event! - - 监听特定区域的特定事件(event)! - 4. Bind a new event over here! - - 在这里绑定一个新事件! - 5. New incoming content! Inject again! Make sure it has the right event bindings! - 又来了新内容。再次插入到 HTML 里,确保它绑定了正确的事件! - - All these things can happen any time from anywhere in an app that's gone spaghetti. Real organization has been given up and it's back to the DOM as the source of truth. It's hard to know exactly what's going on for any given element, so everybody just asks the DOM, does what they need to do, and crosses their fingers it doesn't mess with somebody else. -此类事情可以发生在一个 app 的任何地方、任何时间,这就造成了面条式代码。手动管理是不靠谱的,因为这么做的话又变成 DOM 数据源了。很难准确的知道任何给定的元素发生了什么,所以每个人只好直接查询 DOM ,做他们必须做的事情,顺便向上帝祈祷他们这么做没干扰到别人。 - React says: you don't get to deal with the DOM directly. I have a virtual DOM and I deal with that. Events are bound directly to the elements, and if you need it to do something above and beyond something directly handle-able in this module, you can kind of ceremoniously call things in higher order modules, but that way, the breadcrumb trail can be followed. -React 框架说:你不需要直接操作 DOM 。我用虚拟 DOM 来处理真实的 DOM。事件直接绑定在组件内,如果你想在该组件之外做些什么的话,只需要按照规范在父组件里调用就可以了。通过这种方式,所有的逻辑就有迹可循了。 - *Complicated* DOM management is another thing. Imagine a chat app. New chat messages might appear because a realtime database has new data from other chatters and some new messages have arrives. Or you've typed a new message yourself! Or the page is loading for the first time and old messages are being pulled from a local data store so you have something to see right away. Here's [a Twitter thread](https://twitter.com/mjackson/status/849636985740210177) that drives that home. -管理**复杂的** DOM 是另一件 适合 React 框架的事情。想象有一个聊天软件,当数据库接收到其他聊天者传递来的新聊天信息时,在聊天窗口里应该显示这些新的信息。否则你只能自己给自己聊天了!或者当聊天页面第一次被加载的时候,可以从本地数据库里找出几条旧信息显示出来,这样你立刻有东西可以看了。比如说这个[推特例子](https://twitter.com/mjackson/status/849636985740210177)。 - ### ❌ Just because. It's the new hotness. ### -### ❌ 只是因为,React 框架是目前最火的框架。 ### - Learning something for the sake of learning something is awesome. Do that. -为了学习而学习,是非常酷的。坚持下去。 - Building a project for clients and real human being users requires more careful consideration. -为了满足用户的需求而构建项目则需要更谨慎一点。 - A blog, for example, *probably* has none of the problems and fits none of the scenarios that would make React a good fit. And because it's not a good fit, it's probably a *bad* fit, because it introduces complicated technology and dependencies for something that doesn't call for it. -举个例子,一个博客**也许**没什么复杂的逻辑,一点也不符合应该使用 React 框架的情况。所以如果不是很适合的话,那么也许就是**很不**适合 React 框架。因为这么做引入了复杂的技术,依赖了很多根本没用到的东西。 - And yet, gray area. If that blog is a SPA ("Single Page App", e.g. no browser refreshing) that is built from data from a headless CMS and had fancy server-side rendering... well maybe that is React territory again. -在完全适合和完全不适合之间,如果这个博客是一个 SPA (“单页面应用”,不需要浏览器刷新),通过 headless CMS 获取数据构建该博客,并且具有出色的服务端渲染...好吧,也许又是 React 框架的领域。 - The web app CMS that makes that blog? Maybe a good choice for React, because of all the state. -网络 app 内容管理系统创建的这种博客?也许用 React 是一个好选择,因为也是一大堆的状态。 - ### ❌ I just like JavaScript and want to write everything in JavaScript. ### -### ❌ 我就是喜欢 JavaScript ,就是想用它来编写任何东西。 ### - People get told, heck, I've told people: learn JavaScript. It's huge. It powers all kinds of stuff. There are jobs in it. It's not going anyway. -我经常安利周围的人:学习 JavaScript。因为 JavaScript 的知识太丰富了。它能做很多很多的事情,所以好好学习 JavaScript 永远不会过时。 - It's only in recent web history that it's become possible to never leave JavaScript. You got Node.js on the server side. There are loads of projects that yank CSS out of the mix and handle styles through JavaScript. And with React, your HTML is in JavaScript too. -只有在最近的网络发展中,Javascript 才变的不可或缺。你通过 Node.js 构建服务端,也有很多项目可以通过 JavaScript 处理 CSS。现在通过 React 框架,你还可以通过 JavaScript 来操作 HTML。 - All JavaScript! All hail JavaScript! -万物归于 JavaScript!JavaScript 万岁! - That's cool and all, but again, just because you can doesn't mean you should. Not all projects call for this, and in fact, most probably don't. -JavaScript 确实碉堡了,但是你可以这么做并不意味着你必须这么做。并不是所有的项目都必须使用 JavaScript ,而且事实上,有相当一部分有可能压根不需要。 - ### ☯️ That's what I know. ### -### ☯️ 这就是我所知道的。 ### - (There are decent emojis for YES and NO, but MAYBE is tougher!) -(**是**和**否**有体面的表情,但是**也许**更坚韧!) - - You're learning. Awesome. Everybody is. Keep learning. The more you know the more informed decisions you can make about what tech to use. -你在学习,太好了。每个人都在学习,所以坚持学习吧。你知道的越多,你越知道该应该用什么技术。 - But sometimes you gotta build with what you know, so I ain't gonna ding ya for that. -但是很多时候你只能以现有的技术来构建项目,所以我也不会反复强调这一点。 - ### ☯️ That's where the jobs are. ### -### ☯️ 这就是工作。 ### - Not everybody has a direct say in what technology is used on any given project. Hopefully, over time, you have influence in that, but that takes time. Eden says she [spent 2 years with Ember](https://twitter.com/edenthecat/status/849640183360352257) because that's where the jobs were. No harm in that. Everybody's gotta get paid, and Ember might have been a perfect fit for those projects. -在给定的任何项目中,不是每个人都决定应该底用什么技术。希望随着时间的增长,你可以更大层度上的影响决策。Eden 说她[花了两年的时间研究 Ember](https://twitter.com/edenthecat/status/849640183360352257),因为这就是她的工作。没有任何冒犯的意思,但是拿人钱财就得替人消灾。Ember 也许比较适合这些项目。 --- From dd22a599d40e9db3df62eed29b537002ec15cfcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ARX-8=5C=E6=AC=A7=E9=98=B3=E5=A4=8F=E6=98=B1?= Date: Sun, 30 Apr 2017 11:34:27 +0800 Subject: [PATCH 298/638] =?UTF-8?q?2017.4.30=20=E5=88=9D=E7=BF=BB=E5=AE=8C?= =?UTF-8?q?=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO/project-need-react.md | 121 +++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/TODO/project-need-react.md b/TODO/project-need-react.md index f861a660328..f451a421379 100644 --- a/TODO/project-need-react.md +++ b/TODO/project-need-react.md @@ -6,114 +6,235 @@ # When Does a Project Need React? # +# 项目什么时候需要 React 框架呢? # + You know when a project needs HTML and CSS, because it's all of them. When you reach for JavaScript is fairly clear: when you need interactivity or some functionality that only JavaScript can provide. It used to be fairly clear when we reached for libraries. We reached for jQuery to help us simplify working with the DOM, Ajax, and handle cross-browser issues with JavaScript. We reached for underscore to give us helper functions that the JavaScript alone didn't have. +你知道什么时候项目需要 HTML 和 CSS,因为这是项目的基础。什么时候用 JavaScript 也很清楚:当你需要只有它能提供的交互功能的时候。过去我们什么时候应该用代码库也很清楚:我们需要 jQuery 来帮助我们简化 DOM 操作,调用 Ajax,处理浏览器兼容问题;我们需要 Underscore 提供 JavaScript 无法单独提供的函数式编程。 + As the need for these libraries fades, and we see a massive rise in new frameworks, I'd argue it's not as clear **when to reach for them**. At what point do we need React? +但是随着对这些代码库的需求逐渐消失,我们看到很多新兴的框架的大幅增长。我认为就不那么容易确定**何时需要它们**了。比如说,我们什么情况下需要 React 框架? + I'm just going to use React as a placeholder here for kinda large JavaScript framework thingies. Vue, Ember, Svelte... whatever. I understand they aren't all the same, but when to reach for them I find equally nebulous. +在众多的 JavaScript 框架中 —— Vue、Ember、Svelte ... 不管哪一个,我想以 React 框架为例子来探讨它适合什么项目。我明白这些框架并不完全相同,但是使用它们的时机应该是有一些共性的。 + Here's my take. +这是我的做法。 + ### ✅ Because there is lots of state. ### +### ✅ 当项目中有大量的状态的时候 ### + Even "state" is a bit of a nebulous word. Imagine things like this: +即便“状态(state)”这个词也无法完全准确的表达我的意思。想象一下这些情况: + - Which navigation item is active +- 导航栏的哪个栏目正处于激活状态 - Whether a button is disabled or not +- 一个按钮是否被禁用 - The value of an input +- 输入框的值 - Which accordion sections are expanded +- 哪一个下拉框是弹出的状态 - When an area is loading +- 何时加载某个区域 - The user that is logged in and the team they belong to +- 登陆的用户如何进行权限控制 - Whether the thing the user is working on is published, or a draft +- 用户编写的文章是已发布状态还是草稿状态 "Business logic"-type stuff that we regularly deal with. State can also be straight up content: +“业务逻辑” - 我们经常处理的这类东西。状态也可能和内容直接相关: + - All the comments on an article and the bits and bobs that make them up +- 一篇文章中所有的评论,以及零零碎碎的组成它们的东西。 - The currently viewed article and all its metadata +- 当前正在查看的文章,以及该文章的一些属性 - An array of related articles and the metadata for those +- 一系列相关的文章,以及这些文章的属性 - A list of authors +- 一份作者列表 - An an activity log of recent actions a user has taken +- 一份记录用户近期操作的活动日志 React doesn't help you *organize* that state, it just says: I know you need to *deal* with state, so let's just *call* it state and have programmatic ways to set and get that state. +React 框架并没有帮助你**组织**这些状态,它只是说:我知道你需要处理状态的问题,所以我们不如把它设为 state 属性,通过编程的方式进行读写。 + Before React, we might have *thought* in terms of state, but, for the most part, didn't manage it as a direct concept. +在有 React 框架之前,我们也许**考虑过**状态的定义,但是大部分时候并没有把它当作一个直接的概念去管理。 + Perhaps you've heard the phrase "single source of truth"? A lot of times we treated the DOM as our single source of truth. For example, say you need to know if a form on your website is able to be submitted. Maybe you'd check to see if `$(".form input[type='submit']).is(":disabled")` because all your business logic that dealt with whether or not the form could be submitted or not ultimately changed the disabled attribute of that button. So the button became this defacto source of truth for the state of your app. +也许你听说过这个短语“单一数据源”?很多时候我们把 DOM 作为我们的单一数据源。比如说,你需要知道是否可以提交某个表单了。也许你会用 `$(".form input[type='submit']).is(":disabled")` 去检查一下,因为所有影响表单是否可提交的业务逻辑最终都会改变按钮的 disable 属性。所以按钮变成了你的 app 事实上的数据源。 + Or say you needed to figure of the name of the first comment author on an article. Maybe you'd write `$(".comments > ul > li:first > h3.comment-author).text()` because the DOM is the only place that knows that information. +或者说,你需要知道某篇文章的第一个评论者的名字,也许你会这样写 `$(".comments > ul > li:first > h3.comment-author).text()`,因为 DOM 是你唯一可以获得这些信息的地方。 + React kinda tells us: +React 框架这样告诉我们: + 1. Let's start thinking about all that stuff as state. + + 我们把这些所有的东西都想像成状态(state)。 + 2. I'll do ya one better: state is a chunk of JSON, so it's easy to work with and probably works nicely with your back end. + + 我会为你做好一件事:把状态转换为一串 JSON 字符串,这样的话处理起来很容易,也许你的服务端可以处理的很漂亮。 + 3. And one more even better: You build your HTML using bits of that state, and you won't have to deal with the DOM directly at all, I'll handle all that for you (and likely do a better/faster job than you would have.) + 另外还有一个更好的地方是:你可以用这些状态(state)直接构建 HTML ,你根本不需要直接操作 DOM,我都替你处理了(也许比你亲自处理的要更快更好)。 + ### ✅ To Fight Spaghetti. ### +### ✅ 对抗面条式代码 (Spaghetti)### + This is highly related to the state stuff we were just talking about. +这和我们刚才讨论过的状态非常有关系。 + "Spaghetti" code is when code organization and structure has gotten away from you. Imagine, again, a form on your site. It has some business logic stuff that specifically deals with the inputs inside of it. Perhaps there is a number input that, when changed, display the result of some calculation beside it. The form can also be submitted and needs to be validated, so perhaps that code is in a validation library elsewhere. Perhaps you disable the form until you're sure all JavaScript has loaded elsewhere, and that logic is elsewhere. Perhaps when the form is submitted, you get data back and that needs logic and handling. Nothing terribly surprising here, but you can see how this can get confusing quickly. How does a new dev on the project, looking at that form, reason out everything that is going on? +“面条式”代码,指的是代码的组织结构已经脱离你的掌控。再想象一下,假设有这么一个表单,它有一些专门处理表单内输入框的业务逻辑。该表单内有这么一个数字输入框,当这个输入框的值改变的时候,在旁边显示根据该值进行某些计算后的结果。这个表单可以被提交至服务端,因此也需要合法性检查,而也许合法性检查的代码位于其他地方的验证库中。也许在确定某处的 JavaScript 代码全部加载完之前,你还需要禁用此表单,而这个逻辑也在别的地方。也许当表单提交后,你还需要处理一些返回值。没有什么特别让人意外的功能,但是凑在一起就很容易让人蒙圈。如果这个项目由一个新的开发人员接手后,当他看到这个表单时他如何能捋清这些逻辑呢? + React encourages the use of building things into modules. So this form would likely either be a module of its own or comprised of other smaller modules. Each of them would handle the logic that is directly relevant to it. +React 框架鼓励把东西打包成组件。所以这个表单要么自己是一个组件,要么由其他的小组件组成。每一个组件只处理与自己直接相关的逻辑。 + React says: *well, you aren't going to be watching the DOM directly for changes and stuff, because the DOM is mine and you don't get to work with it directly*. Why don't you start thinking of these things as part of the state, change state when you need to, and I'll deal with the rest, rerendering what needs to be rerendered. +React 框架说:**嗯,你不会直接看到 DOM 的变化,因为 DOM 是我的,你无法直接操作它**。为什么你不把这些东西想象成状态的一部分,当需要的时候就改变状态(state)。我会处理其他的事情,重新渲染需要被渲染的界面。 + It should be said that React itself doesn't entirely solve spaghetti. You can still have state in all kinds of weird places, name things badly, and connect things in weird ways. +应该说,只有 React 框架还不足以解决面条式代码。因为状态也可能出现在各个奇怪的地方,或者状态起的名字很糟糕,或者用莫名其妙的方式调用。 + In my limited experience, it's Redux that is the thing that really kills spaghetti. Redux says: I'll handle *all* the important state, totally globally, not module-by-module. I am the absolute source of truth. If you need to change state, there is quite a *ceremony* involved (I've heard it called that, and I like it.) There are reducers and dispatched actions and such. All changes follow the ceremony. +在我有限的经验内,Redux 框架才能真正解决面条式代码的问题。Redux 框架说:我会处理**所有**重要的状态,都是全局的,不是组件依赖的。我才是唯一的数据源。如果你需要改变状态,就要采用特定的**仪式**(我听说它是这么叫的,而且我喜欢这么叫)。通过 reducers 和被分发的(dispatched) actions,所有的改变都会遵循这种仪式。 + If you go the Redux road (and there are variations of it, of course), you end up with really solid code. It's much harder to break things and there are clear trails to follow for how everything is wired together. +如果你准备在项目中加入 Redux(当然也需要一点微小的改变),那么你就可以和硬编码说再见了。通过加入 Redux 框架,组件会变的高内聚,也很容易理清整个需求的逻辑走向了。 + ### ✅ Lots of DOM management. ### +### ✅ 要控制大量的DOM ### + Manually handling the DOM is probably the biggest cause of spaghetti code. +手动处理 DOM 可能是引起面条式代码的最大原因。 + 1. Inject HTML over here! + + 在这里插入一段 HTML ! + 2. Rip something out over here! + + 在这里把某些东西扔出去! + 3. Watch this area for this event! + + 监听特定区域的特定事件(event)! + 4. Bind a new event over here! + + 在这里绑定一个新事件! + 5. New incoming content! Inject again! Make sure it has the right event bindings! + 又来了新内容。再次插入到 HTML 里,确保它绑定了正确的事件! + + All these things can happen any time from anywhere in an app that's gone spaghetti. Real organization has been given up and it's back to the DOM as the source of truth. It's hard to know exactly what's going on for any given element, so everybody just asks the DOM, does what they need to do, and crosses their fingers it doesn't mess with somebody else. +此类事情可以发生在一个 app 的任何地方、任何时间,这就造成了面条式代码。手动管理是不靠谱的,因为这么做的话又变成 DOM 数据源了。很难准确的知道任何给定的元素发生了什么,所以每个人只好直接查询 DOM ,做他们必须做的事情,顺便向上帝祈祷他们这么做没干扰到别人。 + React says: you don't get to deal with the DOM directly. I have a virtual DOM and I deal with that. Events are bound directly to the elements, and if you need it to do something above and beyond something directly handle-able in this module, you can kind of ceremoniously call things in higher order modules, but that way, the breadcrumb trail can be followed. +React 框架说:你不需要直接操作 DOM 。我用虚拟 DOM 来处理真实的 DOM。事件直接绑定在组件内,如果你想在该组件之外做些什么的话,只需要按照规范在父组件里调用就可以了。通过这种方式,所有的逻辑就有迹可循了。 + *Complicated* DOM management is another thing. Imagine a chat app. New chat messages might appear because a realtime database has new data from other chatters and some new messages have arrives. Or you've typed a new message yourself! Or the page is loading for the first time and old messages are being pulled from a local data store so you have something to see right away. Here's [a Twitter thread](https://twitter.com/mjackson/status/849636985740210177) that drives that home. +管理**复杂的** DOM 是另一件 适合 React 框架的事情。想象有一个聊天软件,当数据库接收到其他聊天者传递来的新聊天信息时,在聊天窗口里应该显示这些新的信息。否则你只能自己给自己聊天了!或者当聊天页面第一次被加载的时候,可以从本地数据库里找出几条旧信息显示出来,这样你立刻有东西可以看了。比如说这个[推特例子](https://twitter.com/mjackson/status/849636985740210177)。 + ### ❌ Just because. It's the new hotness. ### +### ❌ 只是因为,React 框架是目前最火的框架。 ### + Learning something for the sake of learning something is awesome. Do that. +为了学习而学习,是非常酷的。坚持下去。 + Building a project for clients and real human being users requires more careful consideration. +为了满足用户的需求而构建项目则需要更谨慎一点。 + A blog, for example, *probably* has none of the problems and fits none of the scenarios that would make React a good fit. And because it's not a good fit, it's probably a *bad* fit, because it introduces complicated technology and dependencies for something that doesn't call for it. +举个例子,一个博客**也许**没什么复杂的逻辑,一点也不符合应该使用 React 框架的情况。所以如果不是很适合的话,那么也许就是**很不**适合 React 框架。因为这么做引入了复杂的技术,依赖了很多根本没用到的东西。 + And yet, gray area. If that blog is a SPA ("Single Page App", e.g. no browser refreshing) that is built from data from a headless CMS and had fancy server-side rendering... well maybe that is React territory again. +在完全适合和完全不适合之间,如果这个博客是一个 SPA (“单页面应用”,不需要浏览器刷新),通过 headless CMS 获取数据构建该博客,并且具有出色的服务端渲染...好吧,也许又是 React 框架的领域。 + The web app CMS that makes that blog? Maybe a good choice for React, because of all the state. +网络 app 内容管理系统创建的这种博客?也许用 React 是一个好选择,因为也是一大堆的状态。 + ### ❌ I just like JavaScript and want to write everything in JavaScript. ### +### ❌ 我就是喜欢 JavaScript ,就是想用它来编写任何东西。 ### + People get told, heck, I've told people: learn JavaScript. It's huge. It powers all kinds of stuff. There are jobs in it. It's not going anyway. +我经常安利周围的人:学习 JavaScript。因为 JavaScript 的知识太丰富了。它能做很多很多的事情,所以好好学习 JavaScript 永远不会过时。 + It's only in recent web history that it's become possible to never leave JavaScript. You got Node.js on the server side. There are loads of projects that yank CSS out of the mix and handle styles through JavaScript. And with React, your HTML is in JavaScript too. +只有在最近的网络发展中,Javascript 才变的不可或缺。你通过 Node.js 构建服务端,也有很多项目可以通过 JavaScript 处理 CSS。现在通过 React 框架,你还可以通过 JavaScript 来操作 HTML。 + All JavaScript! All hail JavaScript! +万物归于 JavaScript!JavaScript 万岁! + That's cool and all, but again, just because you can doesn't mean you should. Not all projects call for this, and in fact, most probably don't. +JavaScript 确实碉堡了,但是你可以这么做并不意味着你必须这么做。并不是所有的项目都必须使用 JavaScript ,而且事实上,有相当一部分有可能压根不需要。 + ### ☯️ That's what I know. ### +### ☯️ 这就是我所知道的。 ### + (There are decent emojis for YES and NO, but MAYBE is tougher!) +(**是**和**否**有体面的表情,但是**也许**更坚韧!) + + You're learning. Awesome. Everybody is. Keep learning. The more you know the more informed decisions you can make about what tech to use. +你在学习,太好了。每个人都在学习,所以坚持学习吧。你知道的越多,你越知道该应该用什么技术。 + But sometimes you gotta build with what you know, so I ain't gonna ding ya for that. +但是很多时候你只能以现有的技术来构建项目,所以我也不会反复强调这一点。 + ### ☯️ That's where the jobs are. ### +### ☯️ 这就是工作。 ### + Not everybody has a direct say in what technology is used on any given project. Hopefully, over time, you have influence in that, but that takes time. Eden says she [spent 2 years with Ember](https://twitter.com/edenthecat/status/849640183360352257) because that's where the jobs were. No harm in that. Everybody's gotta get paid, and Ember might have been a perfect fit for those projects. +在给定的任何项目中,不是每个人都决定应该底用什么技术。希望随着时间的增长,你可以更大层度上的影响决策。Eden 说她[花了两年的时间研究 Ember](https://twitter.com/edenthecat/status/849640183360352257),因为这就是她的工作。没有任何冒犯的意思,但是拿人钱财就得替人消灾。Ember 也许比较适合这些项目。 --- From ccfb32af806845d3e82578a2b9fb8db1c5a4a09a Mon Sep 17 00:00:00 2001 From: zhuzi Date: Sun, 30 Apr 2017 15:25:26 +0800 Subject: [PATCH 299/638] proofreading --- ...t-the-future-of-component-based-styling.md | 26 +++++++------------ 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/TODO/css-in-javascript-the-future-of-component-based-styling.md b/TODO/css-in-javascript-the-future-of-component-based-styling.md index 595ab2ded82..c2fb296be20 100644 --- a/TODO/css-in-javascript-the-future-of-component-based-styling.md +++ b/TODO/css-in-javascript-the-future-of-component-based-styling.md @@ -2,7 +2,7 @@ > * 原文作者:[Jonathan Z. White](https://medium.freecodecamp.com/@JonathanZWhite) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 译者:[bambooom](https://github.com/bambooom) -> * 校对者: +> * 校对者:[Aladdin-ADD](https://github.com/Aladdin-ADD) # JavaScript 中的 CSS:基于组件的样式的未来 @@ -61,7 +61,7 @@ const styles = StyleSheet.create({ }); ``` -Aphrodite 的优势之一是迁移很直观,学习曲线较平缓。类似 `border-radius` 变成 `borderRadius`,值变成字符串,伪类选择器、媒体查询、字体定义都可以正常工作。另外也可以自动添加浏览器引擎前缀。 +Aphrodite 的优势之一是迁移很直观,学习曲线较平缓。类似 `border-radius` 变成 `borderRadius`,值变成字符串,伪类选择器、媒体查询、字体定义都可以正常工作。另外也可以自动添加浏览器引擎前缀。 **下面就是按钮的样子:** @@ -75,7 +75,7 @@ Aphrodite 的优势之一是迁移很直观,学习曲线较平缓。类似 `bo #### 定义排版常数 -在定义常量时,**使用语义化的变量名**。例如,在给字体大小命名时,不要使用 `h2`,使用 `displayLarge` 描述它的作用。类似的,不要给字体权重命名 `600`,使用 `semibold` 描述它的效果。 +在定义常量时,**使用语义化的变量名**。例如,在给字体大小命名时,不要使用 `h2`,使用 `displayLarge` 描述它的作用。类似的,不要给字体粗细命名 `600`,使用 `semibold` 描述它的效果。 ```javascript export const fontSize = { @@ -120,9 +120,9 @@ export const lineHeight = { }; ``` -设置正确的字体大小和行高变量的值是很重要的。这是因为他们直接影响了设计的垂直规律。垂直规律是一个能帮助你实现一致的元素间距的概念。 +设置正确的字体大小和行高变量的值是很重要的。这是因为他们直接影响了设计的垂直韵律。垂直韵律是一个能帮助你实现一致的元素间距的概念。 -想要了解更多有关垂直规律的内容,你可以阅读这篇文章:[为什么垂直规律对排版实践很重要?](https://zellwk.com/blog/why-vertical-rhythms/) +想要了解更多有关垂直韵律的内容,你可以阅读这篇文章:[为什么垂直韵律对排版实践很重要?](https://zellwk.com/blog/why-vertical-rhythms/) ![](https://cdn-images-1.medium.com/max/800/1*Ehj9XMvQ9wJNhxWNqwXfKw.png) @@ -216,12 +216,10 @@ function Parent() { ### 设计基础第二部分:间距 ### -**间距同时控制着设计中的垂直与水平规律**。所以间距对建立视觉设计系统至关重要。和排版部分一样,第一步也是设定间距常量。 +**间距同时控制着设计中的垂直与水平韵律**。所以间距对建立视觉设计系统至关重要。和排版部分一样,第一步也是设定间距常量。 #### 定义间距常量 ### -> When defining spacing constants for the margins between elements, we can adopt a mathematic approach. Using a `spacingFactor` constant, we can generate a set of distances based on a common factor. **This approach ensures that we have logical and consistent spacing between elements.** - 当为元素之间的 margin 定义间距常量时,我们可以采取一种数学方法。使用一个 `spacingFactor` 常量来生成一组距离。**这种方法确保元素之间的间距是有逻辑并且一致的**。 ```javascript @@ -241,9 +239,7 @@ export const spacing = { }; ``` -> The example above uses a linear scale, one to thirteen. However, experiment with different scales and ratios. Designs require different scales based on their purpose, their audience, and the devices they target. As an example,**here are the first six computed distances using the golden ratio** with a `spacingFactor` of eight. - -上面的例子采用了线性关系,从 1 到 13。不管怎样,多试验几种不同的尺度和比例的搭配才能找到合适的方案。目的、受众、目标设备的不同都需要在设计时考虑。**下面是使用黄金比率计算出来的前 6 个距离**,以 8 个 `spacingFactor` 为例。 +上面的例子采用了线性关系,从 1 到 13。不管怎样,多试验几种不同的尺度和比例的搭配才能找到合适的方案。目的、受众、目标设备的不同都需要在设计时考虑。**下面是使用黄金比率计算出来的前 6 个距离**,以 `spacingFactor` 等于 8 为例。 Golden Ratio (1:1.618) @@ -254,8 +250,6 @@ export const spacing = { 8.0 x (1.618 ^ 4) = 54.82 8.0 x (1.618 ^ 5) = 88.71 -> This is what the spacing scale would look like in code. I added a helper function to handle the computation and round off the output to its nearest pixel value. - 下面是在代码中如何写间距比例。我添加了一个帮助处理间距计算结果的函数,它会返回其最近的像素值。 ```javascript @@ -274,7 +268,7 @@ function computeGoldenRatio(spacingFactor, exp) { } ``` -定义好常量后,我们就可以用它们给元素添加间距。**一种方法就是在组件中 import**。 +定义好间距常量后,我们就可以用它们给元素添加间距。**一种方法就是在组件中 import**。 例如,下面我们给 `Button` 组件添加 `marginBottom`。 @@ -318,7 +312,7 @@ export default Spacing; 由于按钮、输入框、卡片等组件可能需要可变的间距,所以这种方法是有效的。例如,表单中的按钮可能比导航栏的按钮需要更大的边距。需要注意的是,如果一个组件始终具有一致的边距,那么在组件内部处理边距更好。 -你可能注意到前面的例子中只使用了 `marginBottom` ,这是因为**在一个方向定义所有的垂直边距可以避免边距合并,并能跟踪垂直规律**。你可以从 Harry Robert 的文章 [单向边距声明](https://csswizardry.com/2012/06/single-direction-margin-declarations/) 中了解更多这方面知识。 +你可能注意到前面的例子中只使用了 `marginBottom` ,这是因为**在一个方向定义所有的垂直边距可以避免边距合并,并能跟踪垂直韵律**。你可以从 Harry Robert 的文章 [单向边距声明](https://csswizardry.com/2012/06/single-direction-margin-declarations/) 中了解更多这方面知识。 最后,你还可以使用间距常量来定义 padding。 @@ -360,7 +354,7 @@ export const styles = StyleSheet.create({ 你可以在 Medium 上找到我,我每周都会发布一篇文章。你也可以在 [Twitter](https://twitter.com/jonathanzwhite) 上关注我,我会在那里发布一些有关设计、前端开发和虚拟现实的随笔。 -*如果你喜欢这篇文章,欢迎给我点赞 ❤ 并分享给朋友,非常感谢!* +**如果你喜欢这篇文章,欢迎给我点赞 ❤ 并分享给朋友,非常感谢!** [![](https://cdn-images-1.medium.com/max/600/1*mxQhZLqG7l5dMLvxYAklgw.png)](http://mrwhite.space/signup) From b1993997601c32a3c2da17d791005195b558ff6e Mon Sep 17 00:00:00 2001 From: zhuzi Date: Sun, 30 Apr 2017 15:27:58 +0800 Subject: [PATCH 300/638] proofreading --- TODO/css-in-javascript-the-future-of-component-based-styling.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO/css-in-javascript-the-future-of-component-based-styling.md b/TODO/css-in-javascript-the-future-of-component-based-styling.md index c2fb296be20..df98d1293d8 100644 --- a/TODO/css-in-javascript-the-future-of-component-based-styling.md +++ b/TODO/css-in-javascript-the-future-of-component-based-styling.md @@ -279,7 +279,7 @@ import { spacing } from '../styles/base/spacing'; const styles = StyleSheet.create({ button: { - marginBottom: spacing.space4, // adding margin using spacing constant + marginBottom: spacing.space4, // 使用间距常量来添加 margin ... }, }); From 31fc4e1ac8b5c548e6203ca8ab840dfbfa296f39 Mon Sep 17 00:00:00 2001 From: zhuzi Date: Sun, 30 Apr 2017 15:30:11 +0800 Subject: [PATCH 301/638] proofreading --- TODO/css-in-javascript-the-future-of-component-based-styling.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO/css-in-javascript-the-future-of-component-based-styling.md b/TODO/css-in-javascript-the-future-of-component-based-styling.md index df98d1293d8..70e65e61825 100644 --- a/TODO/css-in-javascript-the-future-of-component-based-styling.md +++ b/TODO/css-in-javascript-the-future-of-component-based-styling.md @@ -308,7 +308,7 @@ function Spacing(props) { export default Spacing; ``` -这种方法可以将设置边距的任务从子组件转移到父组件上。**这样,子组件变成了对布局无感知的组件,它不需要知道自己将被放置在何处以及与其他元素的关联**。 +这种方法可以将设置边距的任务从子组件转移到父组件上。**这样,子组件就对布局无感知了,它不需要知道将被放置在何处及与其他元素的关联**。 由于按钮、输入框、卡片等组件可能需要可变的间距,所以这种方法是有效的。例如,表单中的按钮可能比导航栏的按钮需要更大的边距。需要注意的是,如果一个组件始终具有一致的边距,那么在组件内部处理边距更好。 From ef0ee56f0656e2d16cdba27709899c58b61ab217 Mon Sep 17 00:00:00 2001 From: lsvih Date: Sun, 30 Apr 2017 16:42:20 +0800 Subject: [PATCH 302/638] Update es6-modules-support-lands-in-browsers-is-it-time-to-rethink-bundling.md --- ...browsers-is-it-time-to-rethink-bundling.md | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/TODO/es6-modules-support-lands-in-browsers-is-it-time-to-rethink-bundling.md b/TODO/es6-modules-support-lands-in-browsers-is-it-time-to-rethink-bundling.md index d5c2d5bed5e..02906e2ba16 100644 --- a/TODO/es6-modules-support-lands-in-browsers-is-it-time-to-rethink-bundling.md +++ b/TODO/es6-modules-support-lands-in-browsers-is-it-time-to-rethink-bundling.md @@ -8,15 +8,15 @@ ![](http://images.contentful.com/256tjdsmm689/3xFvPzCb6wUek00gQAuU6q/0e8221e0e5c673f18d20448a9ba8924a/Contentful_ES6Modules_.png) -最近一段日子,编写高效的 JavaScript 应用变得越来越复杂。早在几年前,大家都还使用脚本连接来减少 HTTP 请求数;后来有了压缩工具,人们为了压缩代码而缩短变量名,甚至连代码的最后一字节都要省出来。 +最近一段日子,编写高效的 JavaScript 应用变得越来越复杂。早在几年前,大家都开始合并脚本来减少 HTTP 请求数;后来有了压缩工具,人们为了压缩代码而缩短变量名,甚至连代码的最后一字节都要省出来。 -今天,我们有了 [tree shaking](https://blog.engineyard.com/2016/tree-shaking) 和各种模块打包器,我们为了不在首屏加载时阻塞主进程又开始进行代码分割,[加快交互时间](https://developers.google.com/web/tools/lighthouse/audits/time-to-interactive)。我们还开始转译一切东西:感谢 Babel,让我们能够在现在就使用未来的特性。 +今天,我们有了 [tree shaking](https://blog.engineyard.com/2016/tree-shaking) 和各种模块打包器,我们为了不在首屏加载时阻塞主进程又开始进行代码分割,加快[交互时间](https://developers.google.com/web/tools/lighthouse/audits/time-to-interactive)。我们还开始转译一切东西:感谢 Babel,让我们能够在现在就使用未来的特性。 -ES6 模块由 ECMAScript 标准制定,[定稿有些时日了](http://2ality.com/2014/09/es6-modules-final.html)。社区为它写了很多的文章,讲解如何通过 Babel 使用它们,以及 `import` 和 Node.js 的 `require` 的区别。但是要在浏览器中真正实现它还需要一点时间。我惊喜地发现 Safari 在它的 technology preview 版本中第一个装载了 ES6 模块,并且 Edge 和 Firefox Nightly 版本也将要支持 ES6 模块——虽然目前还不支持。在使用 `RequireJS` 和 `Browserify` 之类的(还记得关于 [AMD 与 CommonJS 的讨论吗](https://addyosmani.com/writing-modular-js/)?)工具后,浏览器终于能支持模块了。让我们来看看明朗的未来带来了怎样的礼物吧!🎉 +ES6 模块由 ECMAScript 标准制定,[定稿有些时日了](http://2ality.com/2014/09/es6-modules-final.html)。社区为它写了很多的文章,讲解如何通过 Babel 使用它们,以及 `import` 和 Node.js 的 `require` 的区别。但是要在浏览器中真正实现它还需要一点时间。我惊喜地发现 Safari 在它的 technology preview 版本中第一个装载了 ES6 模块,并且 Edge 和 Firefox Nightly 版本也将要支持 ES6 模块——虽然目前还不支持。在使用 `RequireJS` 和 `Browserify` 之类的工具后(还记得关于 [AMD 与 CommonJS 的讨论吗](https://addyosmani.com/writing-modular-js/)?),至少看起来浏览器终于能支持模块了。让我们来看看明朗的未来带来了怎样的礼物吧!🎉 ## 传统方法 ## -现在构建 web 应用的常用方式就是使用由 Browserify、Rollup、Webpack 等工具构建的代码包(bundle)。而不使用 SPA(单页面应用)技术的网站则通常由服务端生成 HTML,在其中引入一个 JavaScript 代码包。 +构建 web 应用的常用方式就是使用由 Browserify、Rollup、Webpack 等工具构建的代码包(bundle)。而不使用 SPA(单页面应用)技术的网站则通常由服务端生成 HTML,在其中引入一个 JavaScript 代码包。 ``` @@ -62,7 +62,7 @@ export default function() { ### 装载一个代码包(bundle) ### -配置使用 Webpack 创建一个代码包相对来说比较简单。在构建过程中,除了打包和使用 UglifyJS 压缩 JavaScript 文件之外并没有做别的什么事。 +配置使用 Webpack 创建一个代码包相对来说比较直观。在构建过程中,除了打包和使用 UglifyJS 压缩 JavaScript 文件之外并没有做别的什么事。 ``` // webpack.config.js @@ -92,7 +92,7 @@ total 24 -rw-r--r-- 1 stefanjudis staff 197B Mar 16 19:33 index.js ``` -在我通过 Webpack 构建之后,我得到了一个 856 字节的代码包,大约增大了 500 字节。增加这么些字节还是可以接受的,这个代码包与我们平常生产环境中做代码装载没啥区别。感谢 Webpack,现在我们可以使用 ES6 模块了。 +在我通过 Webpack 构建之后,我得到了一个 856 字节的代码包,大约增大了 500 字节。增加这么些字节还是可以接受的,这个代码包与我们平常生产环境中做代码装载没啥区别。感谢 Webpack,我们已经可以使用 ES6 模块了。 ``` @@ -107,9 +107,9 @@ bundle.js 856 bytes 0 [emitted] main [2] ./app/index.js 202 bytes {0}[built] ``` -## 使用新设定支持原生 ES6 模块 ## +## 使用原生支持的 ES6 模块的新设定 ## -现在,我们得到了一个“传统的打包代码”,现在所有还不支持 ES6 模块的浏览器都支持这种打包的代码。我们可以开始玩一些有趣的东西了。让我们在 `index.html` 中加上一个新的 script 元素,使用 `type="module"` 指明这是 ES6 模块。 +现在,我们得到了一个“传统的打包代码”,现在所有还不支持 ES6 模块的浏览器都支持这种打包的代码。我们可以开始玩一些有趣的东西了。让我们在 `index.html` 中加上一个新的 script 元素指向 ES6 模块,为其加上 `type="module"`。 ``` @@ -126,7 +126,7 @@ bundle.js 856 bytes 0 [emitted] main ![Bildschirmfoto 2017-03-29 um 17.06.26](http://images.contentful.com/256tjdsmm689/1mefe0J3JKOiAoSguwMkka/0d76c5666300ed0b631a0fe548ac5b52/Bildschirmfoto_2017-03-29_um_17.06.26.png) -遗憾的是,它并没有显示另外的“Hello world”。造成问题的原因是构建工具与原生 ES 模块的差异:Webpack 是在构建的过程中找到那些文件需要 include 的,而 ES 模块是在浏览器中运行的时候才去取文件的,因此我们需要为此指定正确的文件路径: +遗憾的是,它并没有显示另外的“Hello world”。造成问题的原因是构建工具与原生 ES 模块的差异:Webpack 是在构建的过程中找到那些需要 include 的文件,而 ES 模块是在浏览器中运行的时候才去取文件的,因此我们需要为此指定正确的文件路径: ``` // app/index.js @@ -167,7 +167,7 @@ import dep1 from './dep-1.js'; ### 模块与脚本的不同 ### -这儿有几个问题。首先,JavaScript 在 ES6 模块中运行与平常在 script 元素中不同。Axel Rauschmayer 在[他的探索 ES6 一书](http://exploringjs.com/es6/ch_modules.html#sec_modules-vs-scripts)中很好地讨论了这个问题。我推荐你点击上面的链接阅读这本书,但是在此我先快速地总结一下主要的不同点: +这儿有几个问题。首先,JavaScript 在 ES6 模块中运行与平常在 script 元素中不同。Axel Rauschmayer 在他的[探索 ES6](http://exploringjs.com/es6/ch_modules.html#sec_modules-vs-scripts)一书中很好地讨论了这个问题。我推荐你点击上面的链接阅读这本书,但是在此我先快速地总结一下主要的不同点: - ES6 模块默认在严格模式下运行(因此你不需要加上 `use strict` 了)。 - 最外层的 `this` 指向 `undefined`(而不是 window)。 @@ -262,11 +262,11 @@ $ ll dist/modules 那如果我们像之前在浏览器中对代码进行预加载那样,用 `` 元素告知浏览器要加载额外的 request,是否会加快模块的加载速度呢?在 Webpack 中,我们已经有了类似的工具,比如 Addy Osmani 的 [Webpack 预加载插件](https://github.com/GoogleChrome/preload-webpack-plugin)可以对分割的代码进行预加载,那 ES6 模块有没有类似的方法呢?如果你还不清楚 `rel="preload"` 是如何运作的,你可以先阅读 Yoav Weiss 在 Smashing Magazine 发表的相关文章:[点击阅读](https://www.smashingmagazine.com/2016/02/preload-what-is-it-good-for/) -但是,ES6 模块的预加载并不是那么简单,他们与普通的脚本有很大的不同。那么问题来了,对一个 link 元素加上 `rel="preload"` 将会怎样处理 ES6 模块呢?它也会取出所有的依赖文件吗?这个问题显而易见(可以),但是使用 `preload` 命令加载模块,需要解决更多浏览器的内部实现问题。[Domenic Denicola](https://twitter.com/domenic) 在[一个 GitHub issue](https://github.com/whatwg/fetch/issues/486) 中讨论了这方面的问题,如果你感兴趣的话可以点进去看一看。但是事实证明,使用 `rel="preload"` 加载脚本与加载 ES6 模块是截然不同的。可能以后最终的解决方案是用另一个 `rel="modulepreload"` 命令来专门加载模块。在本文写作时,[这个 pull request](https://github.com/whatwg/html/pull/2383) 还在审核中,你可以点进去看看未来我们可能会怎样进行模块的预加载。 +但是,ES6 模块的预加载并不是那么简单,他们与普通的脚本有很大的不同。那么问题来了,对一个 link 元素加上 `rel="preload"` 将会怎样处理 ES6 模块呢?它也会取出所有的依赖文件吗?这个问题显而易见(可以),但是使用 `preload` 命令加载模块,需要解决更多浏览器的内部实现问题。[Domenic Denicola](https://twitter.com/domenic) 在[一个 GitHub issue](https://github.com/whatwg/fetch/issues/486) 中讨论了这方面的问题,如果你感兴趣的话可以点进去看一看。但是事实证明,使用 `rel="preload"` 加载脚本与加载 ES6 模块是截然不同的。可能以后最终的解决方案是用另一个 `rel="modulepreload"` 命令来专门加载模块。在本文写作时,[这个 pull request](https://github.com/whatwg/html/pull/2383) 还在审核中,你可以点进去看看未来我们可能会怎样进行模块的预加载。 -## 加入真实情况中的依赖 ## +## 加入真实的依赖 ## -仅仅 3 个文件当然没法做一个真正的 app,所以让我们给它加一些真实情况中的依赖。[Lodash](https://lodash.com/) 根据 ES6 模块对它的功能进行了分割,并分别提供给用户。我取出其中一个功能,然后使用 Babili 进行压缩。现在让我们对 `index.js` 文件进行修改,引入这个 Lodash 的方法。 +仅仅 3 个文件当然没法做一个真正的 app,所以让我们给它加一些真实的依赖。[Lodash](https://lodash.com/) 根据 ES6 模块对它的功能进行了分割,并分别提供给用户。我取出其中一个功能,然后使用 Babili 进行压缩。现在让我们对 `index.js` 文件进行修改,引入这个 Lodash 的方法。 ``` @@ -317,7 +317,7 @@ export const unneededStuff = [ ]; ``` -Babili 将会简单地压缩文件,比如 Safari Preview 还是会接收到这几行没有用过的代码。而另一方面,Webpack 打的包或者 Rollup 打的包将不会包含这个 `unnededStuff`。Tree shaking 省略了大量代码,毫无疑问,它能够在真实的产品代码库中很好地发挥作用。 +Babili 只会压缩文件, Safari Preview 在这种情况下会接收到这几行没有用过的代码。而另一方面,Webpack 或者 Rollup 打的包将不会包含这个 `unnededStuff`。Tree shaking 省略了大量代码,它毫无疑问应当被用在真实的产品代码库中。 ## 尽管未来很明朗,但是现在的构建过程仍然不会变动 ## From b175aaba43bd0f851416d7caec0083562a537649 Mon Sep 17 00:00:00 2001 From: zhuzi Date: Sun, 30 Apr 2017 17:45:37 +0800 Subject: [PATCH 303/638] proofreading & add proofreaders --- ...javascript-the-future-of-component-based-styling.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/TODO/css-in-javascript-the-future-of-component-based-styling.md b/TODO/css-in-javascript-the-future-of-component-based-styling.md index 70e65e61825..59ce3162744 100644 --- a/TODO/css-in-javascript-the-future-of-component-based-styling.md +++ b/TODO/css-in-javascript-the-future-of-component-based-styling.md @@ -2,7 +2,7 @@ > * 原文作者:[Jonathan Z. White](https://medium.freecodecamp.com/@JonathanZWhite) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 译者:[bambooom](https://github.com/bambooom) -> * 校对者:[Aladdin-ADD](https://github.com/Aladdin-ADD) +> * 校对者:[Aladdin-ADD](https://github.com/Aladdin-ADD)、[reid3290](https://github.com/reid3290) # JavaScript 中的 CSS:基于组件的样式的未来 @@ -12,7 +12,7 @@ 使用行内样式使我们可以获得 JavaScript 的所有编程支持。这让我们获得类似 CSS 预处理器(变量、混入和函数)的好处,它也解决了 CSS 的很多问题,如全局命名空间和样式冲突。 -如果想要更深入了解 JavaScript 中的 CSS 所解决的问题,可以查看著名的演示幻灯:[React:JS 中的 CSS](https://speakerdeck.com/vjeux/react-css-in-js)。有关使用 Aphrodite 性能优化的案例研究,你可以阅读 [行内 CSS 在可汗学院:Aphrodite](http://engineering.khanacademy.org/posts/aphrodite-inline-css.htm)。如果你想了解更多,可以阅读 [Airbnb 的风格指南](https://github.com/airbnb/javascript/tree/master/css-in-javascript)。 +如果想要更深入了解 JavaScript 中的 CSS 所解决的问题,可以查看著名的演示幻灯:[React:JS 中的 CSS](https://speakerdeck.com/vjeux/react-css-in-js)。有关使用 Aphrodite 性能优化的案例研究,你可以阅读 [行内 CSS 在可汗学院:Aphrodite](http://engineering.khanacademy.org/posts/aphrodite-inline-css.htm)。如果想要学习更多有关 JavaScript 中的 CSS 的最佳实践,可以阅读 [Airbnb 的风格指南](https://github.com/airbnb/javascript/tree/master/css-in-javascript)。 此外,我们将使用行内 JavaScript 样式来构建组件,以解决我之前的一篇文章([掌握设计之前,必须掌握基本原理](https://medium.freecodecamp.com/before-you-can-master-design-you-must-first-master-the-fundamentals-1981a2af1fda))中涉及的一些基础设计问题。 @@ -287,7 +287,7 @@ const styles = StyleSheet.create({ 多数情况下这都是有效的。但是如果我们想要根据按钮的位置来修改它的 `marginBottom` 属性呢? -实现可变边距的一种方法是覆盖从父组件继承的样式。另一种方法是**创建一个 **`Spacing` **组件来控制元素的垂直边距**。 +实现可变边距的一种方法是覆盖从父组件继承的样式。另一种方法是**创建一个 `Spacing` 组件来控制元素的垂直边距**。 ```javascript import React, { PropTypes } from 'react'; @@ -344,11 +344,11 @@ export const styles = StyleSheet.create({ 对 margin 和 padding 使用相同的间距常量,可以在设计中实现更好的视觉一致性。 -结果可能大致如下: +结果大致如下: ![](https://cdn-images-1.medium.com/max/800/1*oDkbVmgCJ4ss5fuRNvzoUg.png) -现在你已经大致了解了 JavaScript 中的 CSS 了,去试验一下吧。尝试在下个项目中采用行内 JavaScript 样式吧。我想**你会喜欢上能够在同一个上下文中处理所有样式及视图问题的感觉**。 +现在你已经大致了解 JavaScript 中的 CSS 了,去试验一下吧。尝试在下个项目中采用行内 JavaScript 样式吧。我想**你会喜欢上能够在同一个上下文中处理所有样式及视图问题的感觉**。 有关 CSS 和 JavaScript 的主题中,你对什么新的发展感兴趣呢?我个人对 async/await 非常感兴趣。给我留言或者在 [Twitter](https://twitter.com/jonathanzwhite) 上发信息给我吧。 From 9cf7db05e5224b78d44674f1479d822c50911adb Mon Sep 17 00:00:00 2001 From: Romeo Date: Sun, 30 Apr 2017 23:06:04 +0800 Subject: [PATCH 304/638] =?UTF-8?q?=E7=BF=BB=E8=AF=91=E5=88=9D=E7=A8=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO/designing-html-apis.md | 102 +++++++++++++++++++++++++++++++++++- 1 file changed, 101 insertions(+), 1 deletion(-) diff --git a/TODO/designing-html-apis.md b/TODO/designing-html-apis.md index b185c4b7c6b..72eda8f5f61 100644 --- a/TODO/designing-html-apis.md +++ b/TODO/designing-html-apis.md @@ -1,23 +1,35 @@ > * 原文地址:[HTML APIs: What They Are And How To Design A Good One](https://www.smashingmagazine.com/2017/02/designing-html-apis/) * 原文作者:[Lea Verou](https://www.smashingmagazine.com/author/lea-verou/) * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) -* 译者: +* 译者:[王子建](https://github.com/Romeo0906) * 校对者: # HTML APIs: What They Are And How To Design A Good One # +#何为 HTML API,如何设计之?# + As JavaScript developers, we often forget that not everyone has the same knowledge as us. It’s called the [curse of knowledge](https://en.wikipedia.org/wiki/Curse_of_knowledge): When we’re an expert on something, we cannot remember how confused we felt as newbies. We overestimate what people will find easy. Therefore, we think that requiring a bunch of JavaScript to initialize or configure the libraries we write is OK. Meanwhile, some of our users struggle to use them, frantically copying and pasting examples from the documentation, tweaking them at random until they work. +作为 JavaScript 开发者,我们经常忘记并不是所有人都拥有像我们一样的学识。这被称为[知识的诅咒](https://en.wikipedia.org/wiki/Curse_of_knowledge):当我们精通某个内容的时候,我们就不记得自己作为新人的时候有多么困惑。我们总是对人们的能力估计过高。因此,我们认为自己写的那些需要一些 JavaScript 代码去初始化或者配置的类库是完全没问题的。然而,一些用户却在使用中费劲力气,他们疯狂地从文档中复制粘贴例子并随机组合这些代码,直到它们生效。 + You might be wondering, “But all HTML and CSS authors know JavaScript, right?” Wrong. Take a look at the results of [my poll](https://twitter.com/LeaVerou/status/690583334414635009), which is the only data on this I’m aware of. (If you know of any proper studies on this, please mention them in the comments!) +你或许会想,“但是所有写 HTML 和 CSS 的人都懂 JavaScript,对吧?”你错了。来看看我的[测验结果](https://twitter.com/LeaVerou/status/690583334414635009)吧,这是我所知道的唯一数据了。(如果你知道任何适用于这个主题的研究,请在评论中说明!) + ![](https://www.smashingmagazine.com/wp-content/uploads/2017/02/lea-verou-poll-tweet-opt.png) Poll: “How comfortable are you with JavaScript?” ([22 January 2016](https://twitter.com/LeaVerou/status/690583334414635009) +测试:“你对 JavaScript 好感如何?”([2016 年 1 月 22 日](https://twitter.com/LeaVerou/status/690583334414635009)) + One in two people who write HTML and CSS is **not comfortable with JavaScript**. One in two. Let that sink in for a moment. +每两个写 HTML 和 CSS 的人中就有一个**对 JavaScript 并无好感**。每两个中就有一个,让我们感受一下。 + As an example, look at the following code to initialize a jQuery UI autocomplete, taken from [its documentation](https://jqueryui.com/autocomplete/): +举个例子,看一下以下用来初始化一个 jQuery UI 自动完成库的代码,摘自[文档](https://jqueryui.com/autocomplete/)。 + ```
@@ -39,8 +51,12 @@ $( function() { This is easy, even for people who don’t know any JavaScript, right? Wrong. A non-programmer would have all sorts of questions going through their head after seeing this example in the documentation. “Where do I put this code?” “What are these braces, colons and brackets?” “Do I need them?” “What do I do if my element does not have an ID?” And so on. Even this tiny snippet of code requires people to understand object literals, arrays, variables, strings, how to get a reference to a DOM element, events, when the DOM is ready and much more. Things that seem trivial to programmers can be an uphill battle to HTML authors with no JavaScript knowledge. +这很简单,即使对根本不了解 JavaScript 的人来说也是这样,对吧?错。一个非程序员在文档中看到这个例子的时候,脑子里会闪过各种各样的问题。“我该把这段代码放哪儿呢?”“这些花括号、冒号和方括号是些什么鬼?”“我用得着这些吗?”“如果我的元素没有 ID 我该怎么办呢?”等等。即使这个微乎其微的代码段也要求人们了解对象字面量、数组、变量、字符串、如何获取 DOM 元素的引用、事件、 DOM 数何时构建完毕等等更多知识。对于程序员来说微不足道的事情,对没有 JavaScript 知识的 HTML 作者来说都是一场攻坚战。 + Now consider the equivalent declarative code [from HTML5](https://www.w3.org/TR/html5/forms.html#the-datalist-element): +现在来考虑一下 [HTML5](https://www.w3.org/TR/html5/forms.html#the-datalist-element) 中的等效声明性代码: + ```
@@ -57,84 +73,168 @@ Now consider the equivalent declarative code [from HTML5](https://www.w3.org/TR/ Not only is this much clearer to anyone who can write HTML, it is even easier for programmers. We see that everything is set in one place, no need to care about when to initialize, how to get a reference to the element and how to set stuff on it. No need to know which function to call to initialize or which arguments it accepts. And for more advanced use cases, there is also a JavaScript API in place that allows all of these attributes and elements to be created dynamically. It follows one of the most basic API design principles: It makes the simple easy and the complex possible. +这不光对于那些能够写 HTML 的人来说简单了不少,甚至对程序员来说更为简单。我们看到所有的东西都被写在一个地方,不必关心什么时候初始化,如何获取元素的引用和如何设置,无需知道哪个函数是用来初始化或者它需要什么参数。在更高级的使用中,还会在允许动态创建属性和元素的地方添加一个 JavaScript API。这遵循了一条最基本的 API 设计原则:它是的简单的事物变得容易,让复杂的事物变得可能。 + This brings us to **an important lesson about HTML APIs**: They would benefit not only people with limited JavaScript skill. For common tasks, even we, programmers, are often eager to sacrifice the flexibility of programming for the convenience of declarative markup. However, we somehow forget this when writing a library of our own. +这给我们上了一堂**关于 HTML API 的重要课程**:这不光使那些拥有有限的 JavaScript 技能的人受益。对于普通的任务,甚至我们,程序员,也会渴望牺牲程序的灵活性来换取更高的表述性。然而不知怎的,我们在写自己用的类库的时候却总是忘记这些。 + So, what is an HTML API? [According to Wikipedia](https://en.wikipedia.org/wiki/Application_programming_interface), an API (or application programming interface) is “is a set of subroutine definitions, protocols, and tools for building application software.” In an HTML API, the definitions and protocols are in the HTML itself, and the tools look in HTML for the configuration. HTML APIs usually consist of certain class and attribute patterns that can be used on existing HTML. With Web Components, even [custom element names](https://www.w3.org/TR/custom-elements/)are game, and with the [Shadow DOM](https://dom.spec.whatwg.org/#shadow-trees), those can even have an entire internal structure that is hidden from the rest of the page’s JavaScript or CSS. But this is not an article about Web Components; Web Components give more power and options to HTML API designers; but the principles of good (HTML) API design are the same. +那么什么是 HTML API 呢?根据[维基百科](https://en.wikipedia.org/wiki/Application_programming_interface)的定义,API(也就是应用程序接口)是“子程序定义、协议和构建应用软件的工具的合集”。在 HTML API 中,定义和协议都在 HTML 中实现,工具也在 HTML 中配置。HTML API 通常由特定的类和可用于先有的 HTML 的属性模式组成。通过 Web 组件(甚至[自定义元素名称](https://www.w3.org/TR/custom-elements/)都是游戏)和[Shadow DOM](https://dom.spec.whatwg.org/#shadow-trees),HTML API 甚至能拥有完整的内部结构,隐藏在页面其余的 JavaScript 或者 CSS 中。但是这并不是一篇关于 Web 组件的文章,Web 组件给予了 HTML API 设计者更多的能力和选择,但是良好的(HTML)API 设计原则都是相同的。 + HTML APIs improve collaboration between designers and developers, lift some work from the shoulders of the latter, and enable designers to create much higher-fidelity mockups. Including an HTML API in your library does not just make the community more inclusive, it also ultimately comes back to benefit you, the programmer. +HTML API 加强了设计师和工程师之间的合作,减轻工程师肩上的工作负担,还能让设计师创造更具有忠诚度的原型。在类库中引入 HTML API 不仅让社区更具包容性,最终还能反过来让程序员受益。 + **Not every library needs an HTML API.** HTML APIs are mostly useful in libraries that enable UI elements such as galleries, drag-and-drop, accordions, tabs, carousels, etc. As a rule of thumb, if a non-programmer cannot understand what your library does, then your library doesn’t need an HTML API. For example, libraries that simplify or help to organize code do not need an HTML API. What kind of HTML API would an MVC framework or a DOM helper library even have? +**并不是每个类库都需要 HTML API。**HTML API 在使用了 UI 元素的类库中非常有用,比如 galleries、drag-and-drop、accordions、tabs、carousels 等等。第一要务便是,如果一个非程序员不能理解你的类库是干什么的,那么它就不需要 HTML API。比如,那些简化代码或者帮助管理代码的类库就不需要 HTML API。那么 MVC 框架或者 DOM 助手类库应该用什么样的 HTML API 呢? + So far, we have discussed what an HTML API is, why it is useful and when it is needed. The rest of this article is about how to design a good one. +目前为止,我们只讨论了 HTML API 的定义、功能和用处,文章剩下的部分是关于如何设计一个好的 HTML API。 + ### Init Selector ### +### 初始化选择器 ### + With a JavaScript API, initialization is strictly controlled by the library’s user: Because they have to manually call a function or create an object, they control precisely when it runs and on what. With an HTML API, we have to make that choice for them, and make sure not to get in the way of the power users who will still use JavaScript and want full control. +在 JavaScript API 中,初始化是被类库的用户严格控制的:因为他们必须手动调用函数或者创建对象,他们精确地控制着运行的时间和基础。在 HTML API 中,我们必须帮用户做选择并且还要确保不会妨碍那些强势的仍然使用 JacaScript 并且想要得到完全控制权的用户。 + The common way to resolve the tension between these two use cases is to only auto-initialize elements that match a given selector, usually a specific class. [Awesomplete](http://leaverou.github.io/awesomplete) follows this approach, only picking up input elements with `class="awesomplete"`. +最常见的解决两种使用场景的办法就是只有匹配到一个给定的选择器,通常是一个特定的类,的时候才会自动初始化。[Awesomplete](http://leaverou.github.io/awesomplete) 就是采用的这种方法,只选取输入的具有 `class="awesomplete"` 的元素。 + In some cases, making auto-initialization easy is more important than making opt-in explicit. This is common when your library needs to run on a lot of elements, and when avoiding having to manually add a class to every single one is more important than making opt-in explicit. For example, [Prism](http://prismjs.com/) automatically highlights any `` element that contains a `language-xxx` class (which is what the HTML5 specification [recommends for specifying the language of a code snippet](https://www.w3.org/TR/html51/textlevel-semantics.html#the-code-element)) or that is inside an element that does. This is because it could be included in a blog with a ton of code snippets, and having to go back and add a class to every single one of them would be a huge hassle. +有些时候,把自动初始化变得简单比做详尽的选项说明更重要。这是一个很常见的现象,当你的类库需要在非常多的元素上运行的时候,避免手动给每个单独元素添加类比详细的选项说明更加重要。比如,[Prism](http://prismjs.com/) 自动高亮任何包含 `language-xxx` 类的 `` 元素(HTML5 的说明中[建议指定代码段的语言](https://www.w3.org/TR/html51/textlevel-semantics.html#the-code-element))以及其内部的元素。这是因为 Prism 可能会用在一个有着成千上万的代码段的博客系统中,回过头去给每一个元素添加类会是一项非常巨大的工程。 + In cases where the `init` selector is used very liberally, a good practice is to allow customization of it or allow opting-out of auto-initialization altogether. For example, [Stretchy](https://leaverou.github.io/stretchy)[13](#13) autosizes *every*``, `