From 9d5a3665012356bfc8459e0c8b8cdc1b7b0d1b85 Mon Sep 17 00:00:00 2001 From: luxin1 Date: Sun, 26 May 2019 22:27:16 +0800 Subject: [PATCH 01/68] translation/effiective BLoC pattern for flutter --- TODO1/effective-bloc-pattern.md | 78 ++++++++++++++++----------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/TODO1/effective-bloc-pattern.md b/TODO1/effective-bloc-pattern.md index 86057b9e085..5f17899ede6 100644 --- a/TODO1/effective-bloc-pattern.md +++ b/TODO1/effective-bloc-pattern.md @@ -2,50 +2,50 @@ > * 原文作者:[Sagar Suri](https://medium.com/@sagarsuri56) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/effective-bloc-pattern.md](https://github.com/xitu/gold-miner/blob/master/TODO1/effective-bloc-pattern.md) -> * 译者: +> * 译者 [LucaslEliane](https://github.com/LucaslEliane) > * 校对者: -# Effective BLoC pattern +# 高效地使用 BLoC 模式 -Hey Folks, Its been so long I have written anything about Flutter. After writing two articles on BLoC pattern I was spending time doing analysis on the usage of this pattern by the community and after answering some questions on the implementation of BLoC pattern I saw that there was a lot of confusion among people. So I came up with a list of **“Rules of thumb”** that can be followed to properly implement the BLoC pattern which will help a developer to avoid making common mistakes while implementing it. So today I present to you a list of **8 golden points** that must be followed when working with BLoC. +朋友们,我已经写了很多关于 Flutter 的文章。在完成了两篇关于 BLoC 模式的文章之后,我花了一些时间,分析了社区对于这种模式的使用情况,在回答了一些关于 BLoC 模式实现的一些问题之后,我发现大家对于 BLoC 模式存在很多疑惑。所以,我提出了一系列*“经验方法”*,可以遵循这一系列方法来正确地实现 BLoC 模式,这会帮助开发人员在实现的时候避免犯下一些常见的错误。所以,我今天向大家介绍一下在使用 BLoC 模式时必须要遵循的 * 8 个黄金点*。 ![](https://cdn-images-1.medium.com/max/3770/1*XUDik4jakpEcQ6ZVm5jo3A@2x.png) -## Prerequisites +## 前提 -The audience I expect should know what BLoC pattern is or have created an app using the pattern(at least did `CTRL + C` and `CTRL + V`). If this is the first time you heard the word “**BLoC”** then the below three articles would be the perfect place to start understanding this pattern: +我希望读者应该知道 BLoC 模式是什么,或者使用模式创建了一个应用(至少做过 `CTRL + C` 和 `CTRL + V`)。如果你是第一次听到 **BLoC** 这个词,那么下面三篇文章可以很好地帮助你理解这个模式。 -1. Architect your Flutter project using BLoC pattern [**PART 1**](https://medium.com/flutterpub/architecting-your-flutter-project-bd04e144a8f1) and [**PART 2**](https://medium.com/flutterpub/architect-your-flutter-project-using-bloc-pattern-part-2-d8dd1eca9ba5) +1. 使用 BLoC 模式构建 Flutter 项目[第一部分](https://medium.com/flutterpub/architecting-your-flutter-project-bd04e144a8f1)和[第二部分](https://medium.com/flutterpub/architect-your-flutter-project-using-bloc-pattern-part-2-d8dd1eca9ba5) -2. [**When Firebase meets BLoC pattern**](https://medium.com/flutterpub/when-firebase-meets-bloc-pattern-fb5c405597e0) +2. [当 Firebase 遇到了 BLoC 模式](https://medium.com/flutterpub/when-firebase-meets-bloc-pattern-fb5c405597e0) -## Story of those who encountered BLoC +## 和 BLoC 相遇的故事 -I know I know it is a tough pattern to understand and implement. I have seen many posts from developers asking “**Which is the best resource to learn BLoC pattern?**” After going through all the different posts and comments I feel the following points are the common hurdles every single person went through when understanding this pattern. +我知道,BLoC 模式是一个很难去理解和实现的模式。我看过了很多开发人员的帖子,问*哪里是学习 BLoC 模式的最佳资源呢?*,看过了不同的帖子和评论之后,我觉得以下几点是每个人在理解这个问题时常见的阻碍。 -1. Thinking reactively. +1. 响应式地思考。 -2. Struggling to understand how many BLoC files need to be created. +2. 努力了解需要创建多少 BLoC 文件。 -3. Scared whether this will scale or not. +3. 害怕这个模式的范围会扩大。 -4. Don’t know when the streams will get disposed. +4. 不知道 stream 在什么时候会被处理掉。 -5. What is the full form of BLoC? (It’s Business Logic Component 😅) +5. 什么是 BLoC 模式的完整形式?(这是一个业务逻辑组件) -6. Many more…. +6. 更多其他的原因…… -But today I will list down some of the most important points that will help you implement BLoC pattern confidently and efficiently. Without any further delay let’s look at those amazing points. +但是今天我要列出一些最为重要的点,这些点可以帮助你更加自信及有效地实现 BLoC 模式。现在,就让我们赶快看看有哪些很棒的点。 -## Every screen has its own BLoC +## 每一个页面都有其自己的 BLoC -This is the most important point to remember. Whenever you create a new screen e.g Login screen, Registration screen, Profile screen etc which involves dealing with data, you have to **create a new BLoC** for it. Don’t use a global BLoC for all the screens in your app. You must be thinking that if I have a common BLoC I can easily use the data between the two screens. That’s not good because your repository should be responsible for providing those common data to the BLoC. BLoC will just take that data and provide to your screen in a manner which can be displayed to the user. +这是需要记住的最终要的一个点。每当你创建了一个新的页面,例如登录页,注册页,配置文件页等涉及到数据处理的页面的时候,你必须要为其*创建一个新的 BLoC*。不要将全局 BLoC 用于处理应用中的所有页面。你可能会认为,如果我们有一个全局的 BLoC,就可以轻松的处理跨页面的数据了。这很不好,因为你的库应当将这些公共数据提供给 BLoC。BLoC 获取数据并且将其注入到页面中,来向用户展示。 -![The left diagram is the correct pattern](https://cdn-images-1.medium.com/max/2000/1*0z3wjE8m89iI4ppbeNe2Jg.png) +![左图是正确的使用模式](https://cdn-images-1.medium.com/max/2000/1*0z3wjE8m89iI4ppbeNe2Jg.png) -## Every BLoC must have a dispose() method +## 每个 BLoC 必须要有一个 dispose() 方法 -This is pretty straight forward. Every BLoC you create should have a `dispose()` method. This is the place where you do the cleanup or close all the streams you have created. A simple example of the `dispose()` method is shown below. +这一点比较直接。你创建的每个 BLoC 都应该有一个 `dispose()` 方法。这个方法是你清理或者关闭你创建的所有 stream 的位置。下面是一个 `dispose()` 的简单的例子。 ```dart class MoviesBloc { @@ -65,15 +65,15 @@ class MoviesBloc { } ``` -## Don’t use StatelessWidget with BLoC +## 不要在 BLoC 中使用 StatelessWidget -Whenever you want to create a screen which will pass data to a BLoC or get data from a BLoC **always use `StatefulWidget`** . The biggest advantage of using `StatefulWidget` over `StatelessWidget` are the lifecycle methods available in the `StatefulWidget`. Later down the article, we will talk about the two most important methods to override when working with BLoC pattern. `StatelessWidgets` are good to make a small static part of your screen e.g showing an image or hardcoded text. If you want to see the implementation of BLoC pattern in a `StatelessWidget` check out **PART1** and in **PART2** I showed why I converted from `StatelessWidget` to a `StatefulWidget`. +每当你想要创建一个传递数据到 BLoC 或者从 BLoC 中获取数据的页面的时候,*请使用 `StatefulWidget`*。使用 `StatefulWidget` 相比于使用 `StatelessWidget` 的最大优点在于 `StatefulWidget` 中的生命周期方法。在文章的后面,我们会讨论在使用 BLoC 模式时需要覆盖的两个最重要的方法。`StatelessWidget` 很适合制作页面的小的静态部分,例如显示图像或者是硬编码的文本。如果你想要通过 `StatelessWidget` 实现 BLoC 模式,请看上面推荐的文章的*第一部分*和*第二部分*,我为什么要从 `StatelessWidget` 迁移到 `StatefulWidget`。 -## Override didChangeDependencies() to initialise BLoC +## 重写 didChangeDependencies() 来初始化 BLoC -This is the most crucial method to override in a `StatefulWidget` if you need a `context` at the beginning to initialise a BLoC object. You can think of it as the initializing method(preferred for BLoC initialisation only). You may argue that we even have a `initState()` so why use `didChangeDependencies()` . As per the doc it’s clearly mentioned that it is safe to call [BuildContext.inheritFromWidgetOfExactType](https://docs.flutter.io/flutter/widgets/BuildContext/inheritFromWidgetOfExactType.html) from `didChangeDependencies()` method. A simple example of how to use this method is shown below: +如果你需要在初始化的时候需要一个 `context` 来初始化 BLoC 对象,那么这个方法就是在 `StatefulWidget` 中需要重写的最重要的方法。你可以将其视为初始化方法(仅适用于 BLoC的初始化)。你可以说,我们有 `initState()` 方法,那么为什么我们要使用 `didChangeDependencies()` 方法。根据文档中提到的,从 `didChangeDependencies()` 来调用 [BuildContext.inheritFromWidgetOfExactType](https://docs.flutter.io/flutter/widgets/BuildContext/inheritFromWidgetOfExactType.html) 是安全的。下面是使用这个方法的一个简单的例子: -```dart +```dart @override void didChangeDependencies() { bloc = MovieDetailBlocProvider.of(context); @@ -82,9 +82,9 @@ This is the most crucial method to override in a `StatefulWidget` if you need a } ``` -## Override dispose() method to dispose BLoC +## 重写 dispose() 方法来销毁 BLoC -Just like there is an initializing method, we have been provided with a method where we can dispose of the connections we created in the BLoC. The `dispose()` method is the perfect place to call the BLoC `dispose()` method associated with that particular screen. This method is always called when you leaving the screen(technically when the `StatefulWidget` is getting disposed). Below is a small example of that method: +就和有一个初始化方法一样,我们还需要提供一个方法,来处理我们在 BLoC 中创建的连接。`dispose()` 方法是调用与该页面相连的对应的 BLoC 的 `dispose()` 方法的最佳位置。每当你离开页面的时候,需要调用这个方法(实际上就是`StatefulWidget`被处理掉的时候)。 以下是该方法的一个小例子: ```dart @override @@ -94,9 +94,9 @@ Just like there is an initializing method, we have been provided with a method w } ``` -## Use RxDart only when dealing with complex logic +## 只有当有复杂逻辑需要处理的时候,才使用 RxDart -If you have worked with BLoC pattern earlier then you must have heard about `[RxDart](https://github.com/ReactiveX/rxdart)` library. It is a reactive functional programming library for Google Dart. This library is just a wrapper over the `Stream` API provided by Dart. I would advise you to use this library only when you are dealing with complex logic like chaining multiple network requests. But for simple implementations use the `Stream` API provided by the Dart language as it is quite mature. Below I have added a BLoC which uses `Stream` API rather than the `RxDart` library because the operations are quite simple and I didn’t need an additional library to do the same: +如果你之前使用过 BLoC 模式的话,那么你一定听说过 `[RxDart](https://github.com/ReactiveX/rxdart)` 库。这个库是 Google Dart 的响应式函数式编程库。这个库只是 Dart 提供的一个 `Stream` API 的包装器。我建议你仅在需要处理,类似于链接多个网络请求这样的复杂逻辑时,才使用这个库。对于一些简单的实现,使用 Dart 语言提供的 `Stream` API 就足够了,因为这个 API 已经非常成熟了。下面我添加了一个 BLoC,它使用了 `Stream` API 而不是 `RxDart` 库,这样会让操作变得非常简单,我们不需要额外的库来实现同样的事情: ```dart import 'dart:async'; @@ -153,15 +153,15 @@ class Bloc { } ``` -## Use PublishSubject over BehaviorSubject +## 使用 PublishSubject 代替 BehaviorSubject -This point is more specific for those using the `RxDart` library in their Flutter project. `BehaviorSubject` is a special `StreamController` that captures the latest item that has been added to the controller, and emits that as the first item to any new listener. Even if you call `close()` or `drain()` on the `BehaviorSubject` it will still hold the last item and emit when subscribed. This can be a nightmare for a developer if he/she is not aware of this feature. Whereas `PublishSubject` doesn’t store the last item and is best suited for most of the cases. Check out this [project](https://github.com/SAGARSURI/Goals) to see the `BehaviorSubject` feature in action. Run the app and go to the “Add Goal” screen, enter the details in the form and navigate back. Now again if you visit the “Add Goal” screen you will find the form pre-filled with the data you entered previously. If you are a lazy person like me then look at the video I have attached below: +对于那些在 Flutter 项目中使用 `RxDart` 库的人来说,这一点会更加地明确。`BehaviorSubject` 是一个特殊的 `StreamController`,它会捕获到已经添加到 controller 的最新项,并且将其作为新的 listener 的第一个事件触发。即使你在 `BehaviorSubject` 上调用 `close()` 或者 `drain()`,它仍然会保留最后一项,并且在这个 listener 被订阅的时候触发。如果开发人员不了解这个功能,那么对于开发人员来说,可以说是一个噩梦。而 `PublishSubject` 不会存储最后一项,更加适合于大多数情况。在这个[项目](https://github.com/SAGARSURI/Goals)中,可以查看 `BehaviorSubject` 的功能。运行应用程序,并且跳转到 'Add Goal' 页面,在表单中输入详细信息,并且导航回来。现在,再次访问 'Add Goal' 页面,你就会发现预先填写了你之前输入的数据的表单。如果你是一个像我一样偷懒的人,那么可以看我下面附上的视频: [Goals App Demo](https://youtu.be/N7-C3o_O1jE) -## Proper use of BLoC Providers +## 正确地使用 BLoC Providers -Before I say anything about this point do check the below code snippet(line 9 and 10). +在我说这一点之前,请看下面的代码片(第 9 行和第 10 行)。 ```dart import 'package:flutter/material.dart'; @@ -198,7 +198,7 @@ class MyApp extends StatelessWidget { ``` -You can clearly see that multiple BLoC providers are nested. Now you must be worried that if you keep adding more BLoCs in that same chain then it will be a nightmare and you will conclude that BLoC pattern cannot scale. But let me tell you that there can be a special case(a BLoC only holding the UI configurations which is required across the app) when you need to access multiple BLoCs anywhere down the Widget tree so for such cases the above nesting is completely fine. But I would recommend you to avoid such nesting most of the time and provide the BLoC from where it is actually needed. So for example when you are navigating to a new screen you can use the BLoC provider like this: +你可以清楚地看到,多个 BLoC Provider 是嵌套的。这时候,你可能会担心,如果继续在同一个链中添加更多的 BLoC,会导致一场噩梦,你可能会得出 BLoC 模式无法扩展的结论。但是,让我告诉你,当你需要在 Widget 树中访问多个 BLoC 的时候,可能会有一种特殊的情况(BLoC 只保存应用程序所需要的 UI 配置),因此,对于这种情况,上述的嵌套是完全正常的。但是我建议你在大多数的情况下,还是要避免这种嵌套的,并且只在实际需要的地方提供 BLoC。因此,比如当你需要导航到新的页面的时候,可以像这样使用 BLoC Provider: ```dart openDetailPage(ItemModel data, int index) { @@ -221,13 +221,13 @@ openDetailPage(ItemModel data, int index) { } ``` -This way the `MovieDetailBlocProvider` will provide the BLoC to the `MovieDetail` screen and not to the whole widget tree. You can see that I stored the `MovieDetailScreen` in a new `final variable` to avoid recreation of the `MovieDetailScreen` every time when the keyboard is opened or closed in the `MovieDetailScreen`. +这样,`MovieDetailBlocProvider` 将会为 `MovieDetail` 页面提供 BLoC,而不是整个组件树。你可以看到,我将 `MovieDetailScreen` 存储在一个新的 `final variable` 中,来避免每次在 `MovieDetailScreen` 中打开或者关闭键盘的时候,都会重新创建 `MovieDetailScreen` 的问题。 -## This is not the end +## 还没有结束 -So here we come to the end of this article. But this is not the end of this topic. I will keep adding new points to this every growing list of optimizing the BLoC pattern as I learn better ways of scaling and implementing the pattern. I hope these points will surely help you implement BLoC pattern in a better way. Keep learning and keep coding. :) If you liked the article then show your love by hitting **50 claps 😄 👏 👏**. +虽然这里是本文的结尾了,但并不是这个主题的结尾。随着我学习到更好的扩展和实现模式的方法,我将会不断为这个优化 BLoC 模式的关键点提供新的观点。我希望这些关键点可以帮助你更好地实现 BLoC 模式。Keep learning and keep coding :)。如果你喜欢这篇文章,可以通过点赞来表达你的爱。 -Having any doubt, connect with me at [**LinkedIn**](https://www.linkedin.com/in/sagar-suri/) or follow me at [**Twitter**](https://twitter.com/SagarSuri94). I will try my best to solve all your queries. +有任何疑问,请在 [LinkedIn](https://www.linkedin.com/in/sagar-suri/) 与我联系,或者在 [Twitter](https://twitter.com/SagarSuri94) 上关注我。我会尽我所能解决你的问题。 > 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 From 6dde3510c2077a8cf962da0a01d9b7c24210ad3a Mon Sep 17 00:00:00 2001 From: luxin1 Date: Sun, 7 Jul 2019 14:06:12 +0800 Subject: [PATCH 02/68] feat: merge upstream master --- TODO1/effective-bloc-pattern.md | 59 --------------------------------- 1 file changed, 59 deletions(-) diff --git a/TODO1/effective-bloc-pattern.md b/TODO1/effective-bloc-pattern.md index 1f09e280bd6..69ceed26341 100644 --- a/TODO1/effective-bloc-pattern.md +++ b/TODO1/effective-bloc-pattern.md @@ -2,31 +2,18 @@ > * 原文作者:[Sagar Suri](https://medium.com/@sagarsuri56) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/effective-bloc-pattern.md](https://github.com/xitu/gold-miner/blob/master/TODO1/effective-bloc-pattern.md) -<<<<<<< HEAD -> * 译者 [LucaslEliane](https://github.com/LucaslEliane) -> * 校对者: - -# 高效地使用 BLoC 模式 - -朋友们,我已经写了很多关于 Flutter 的文章。在完成了两篇关于 BLoC 模式的文章之后,我花了一些时间,分析了社区对于这种模式的使用情况,在回答了一些关于 BLoC 模式实现的一些问题之后,我发现大家对于 BLoC 模式存在很多疑惑。所以,我提出了一系列*“经验方法”*,可以遵循这一系列方法来正确地实现 BLoC 模式,这会帮助开发人员在实现的时候避免犯下一些常见的错误。所以,我今天向大家介绍一下在使用 BLoC 模式时必须要遵循的 * 8 个黄金点*。 -======= > * 译者:[LucaslEliane](https://github.com/LucaslEliane) > * 校对者:[portandbridge](https://github.com/portandbridge) # 高效地使用 BLoC 模式 朋友们,我有好长一段时间没有写过 flutter 相关的文章了。在完成了两篇关于 BLoC 模式的文章之后,我花了一些时间,分析了社区对于这种模式的使用情况,在回答了一些关于 BLoC 模式实现的一些问题之后,我发现大家对于 BLoC 模式存在很多疑惑。所以,我构思了一套方法,大家按照这一套方法来做,就可以正确地实现 BLoC 模式了,这会帮助开发人员在实现的时候避免犯下一些常见的错误。所以,我今天向大家介绍一下在使用 BLoC 模式时必须要遵循的 **8 个黄金点**。 ->>>>>>> upstream/master ![](https://cdn-images-1.medium.com/max/3770/1*XUDik4jakpEcQ6ZVm5jo3A@2x.png) ## 前提 -<<<<<<< HEAD -我希望读者应该知道 BLoC 模式是什么,或者使用模式创建了一个应用(至少做过 `CTRL + C` 和 `CTRL + V`)。如果你是第一次听到 **BLoC** 这个词,那么下面三篇文章可以很好地帮助你理解这个模式。 -======= 我心目中的读者,应该知道 BLoC 模式是什么,或者使用模式创建了一个应用(至少做过 `CTRL + C` 和 `CTRL + V`)。如果你是第一次听到 **BLoC** 这个词,那么下面三篇文章可以很好地帮助你理解这个模式。 ->>>>>>> upstream/master 1. 使用 BLoC 模式构建 Flutter 项目[第一部分](https://medium.com/flutterpub/architecting-your-flutter-project-bd04e144a8f1)和[第二部分](https://medium.com/flutterpub/architect-your-flutter-project-using-bloc-pattern-part-2-d8dd1eca9ba5) @@ -34,21 +21,13 @@ ## 和 BLoC 相遇的故事 -<<<<<<< HEAD -我知道,BLoC 模式是一个很难去理解和实现的模式。我看过了很多开发人员的帖子,问*哪里是学习 BLoC 模式的最佳资源呢?*,看过了不同的帖子和评论之后,我觉得以下几点是每个人在理解这个问题时常见的阻碍。 -======= 我知道,BLoC 模式是一个很难去理解和实现的模式。我看过了很多开发人员的帖子,询问 **哪里是学习 BLoC 模式的最佳资源呢**?读完了不同的帖子和评论之后,我觉得大家在理解这个问题的阻碍有以下几点。 ->>>>>>> upstream/master 1. 响应式地思考。 2. 努力了解需要创建多少 BLoC 文件。 -<<<<<<< HEAD -3. 害怕这个模式的范围会扩大。 -======= 3. 害怕这个模式会造成代码复杂度的提升。 ->>>>>>> upstream/master 4. 不知道 stream 在什么时候会被处理掉。 @@ -60,11 +39,7 @@ ## 每一个页面都有其自己的 BLoC -<<<<<<< HEAD -这是需要记住的最终要的一个点。每当你创建了一个新的页面,例如登录页,注册页,配置文件页等涉及到数据处理的页面的时候,你必须要为其*创建一个新的 BLoC*。不要将全局 BLoC 用于处理应用中的所有页面。你可能会认为,如果我们有一个全局的 BLoC,就可以轻松的处理跨页面的数据了。这很不好,因为你的库应当将这些公共数据提供给 BLoC。BLoC 获取数据并且将其注入到页面中,来向用户展示。 -======= 这是需要记住的最重要的一个点。每当你创建了一个新的页面,例如登录页,注册页,个人资料页等涉及到数据处理的页面的时候,你必须要为其 **创建一个新的 BLoC**。不要将全局 BLoC 用于处理应用中的所有页面。你可能会认为,如果我们有一个全局的 BLoC,就可以轻松地处理跨页面的数据了。这很不好,因为你的库应当将这些公共数据提供给 BLoC。BLoC 仅仅是获取数据并且将其注入到页面中,来向用户展示。 ->>>>>>> upstream/master ![左图是正确的使用模式](https://cdn-images-1.medium.com/max/2000/1*0z3wjE8m89iI4ppbeNe2Jg.png) @@ -92,19 +67,11 @@ class MoviesBloc { ## 不要在 BLoC 中使用 StatelessWidget -<<<<<<< HEAD -每当你想要创建一个传递数据到 BLoC 或者从 BLoC 中获取数据的页面的时候,*请使用 `StatefulWidget`*。使用 `StatefulWidget` 相比于使用 `StatelessWidget` 的最大优点在于 `StatefulWidget` 中的生命周期方法。在文章的后面,我们会讨论在使用 BLoC 模式时需要覆盖的两个最重要的方法。`StatelessWidget` 很适合制作页面的小的静态部分,例如显示图像或者是硬编码的文本。如果你想要通过 `StatelessWidget` 实现 BLoC 模式,请看上面推荐的文章的*第一部分*和*第二部分*,我为什么要从 `StatelessWidget` 迁移到 `StatefulWidget`。 - -## 重写 didChangeDependencies() 来初始化 BLoC - -如果你需要在初始化的时候需要一个 `context` 来初始化 BLoC 对象,那么这个方法就是在 `StatefulWidget` 中需要重写的最重要的方法。你可以将其视为初始化方法(仅适用于 BLoC的初始化)。你可以说,我们有 `initState()` 方法,那么为什么我们要使用 `didChangeDependencies()` 方法。根据文档中提到的,从 `didChangeDependencies()` 来调用 [BuildContext.inheritFromWidgetOfExactType](https://docs.flutter.io/flutter/widgets/BuildContext/inheritFromWidgetOfExactType.html) 是安全的。下面是使用这个方法的一个简单的例子: -======= 每当你想要创建一个传递数据到 BLoC 或者从 BLoC 中获取数据的页面的时候,**请使用 `StatefulWidget`** 。使用 `StatefulWidget` 相比于使用 `StatelessWidget` 的最大优点在于 `StatefulWidget` 中的生命周期方法。在文章的后面,我们会讨论在使用 BLoC 模式时需要覆盖的两个最重要的方法。`StatelessWidget` 很适合制作页面的小的静态部分,例如显示图像或者是硬编码的文本。如果你想要看看怎么用 `StatelessWidget` 来实现 BLoC 模式,请看上面推荐的文章的 **第一部分**,而在**第二部分**中,我讲述了自己为什么要从 `StatelessWidget` 迁移到 `StatefulWidget`。 ## 重写 didChangeDependencies() 来初始化 BLoC 如果你需要在初始化的时候需要一个 `context` 来初始化 BLoC 对象,那么这个方法就是在 `StatefulWidget` 中需要重写的最重要的方法。你可以将其视为初始化方法(最好仅用于 BLoC 的初始化)。你或许会说,我们有 `initState()` 方法,那么为什么我们要使用 `didChangeDependencies()` 方法。文档里面清楚地提到,从 `didChangeDependencies()` 调用 [BuildContext.inheritFromWidgetOfExactType](https://docs.flutter.io/flutter/widgets/BuildContext/inheritFromWidgetOfExactType.html) 是安全的。下面是使用这个方法的一个简单的例子: ->>>>>>> upstream/master ```dart @override @@ -117,11 +84,7 @@ class MoviesBloc { ## 重写 dispose() 方法来销毁 BLoC -<<<<<<< HEAD -就和有一个初始化方法一样,我们还需要提供一个方法,来处理我们在 BLoC 中创建的连接。`dispose()` 方法是调用与该页面相连的对应的 BLoC 的 `dispose()` 方法的最佳位置。每当你离开页面的时候,需要调用这个方法(实际上就是`StatefulWidget`被处理掉的时候)。 以下是该方法的一个小例子: -======= 就和有一个初始化方法一样,我们还有一个方法,来处理掉我们在 BLoC 中创建的连接。`dispose()` 方法是调用与该页面相连的对应的 BLoC 的 `dispose()` 方法的最佳位置。每当你离开页面的时候,需要调用这个方法(实际上就是`StatefulWidget`被处理掉的时候)。以下是该方法的一个小例子: ->>>>>>> upstream/master ```dart @override @@ -131,15 +94,9 @@ class MoviesBloc { } ``` -<<<<<<< HEAD -## 只有当有复杂逻辑需要处理的时候,才使用 RxDart - -如果你之前使用过 BLoC 模式的话,那么你一定听说过 `[RxDart](https://github.com/ReactiveX/rxdart)` 库。这个库是 Google Dart 的响应式函数式编程库。这个库只是 Dart 提供的一个 `Stream` API 的包装器。我建议你仅在需要处理,类似于链接多个网络请求这样的复杂逻辑时,才使用这个库。对于一些简单的实现,使用 Dart 语言提供的 `Stream` API 就足够了,因为这个 API 已经非常成熟了。下面我添加了一个 BLoC,它使用了 `Stream` API 而不是 `RxDart` 库,这样会让操作变得非常简单,我们不需要额外的库来实现同样的事情: -======= ## 只有需要处理复杂逻辑的时候,才使用 RxDart 如果你之前使用过 BLoC 模式的话,那么你一定听说过 `[RxDart](https://github.com/ReactiveX/rxdart)` 库。这个库是 Google Dart 的响应式函数式编程库,它只是一个包装器,用来包装 Dart 提供的 `Stream` API。我建议你仅在需要处理,类似于链接多个网络请求这样的复杂逻辑时,才使用这个库。对于一些简单的实现,使用 Dart 语言提供的 `Stream` API 就足够了,因为这个 API 已经非常成熟了。下面我添加了一个 BLoC,它使用了 `Stream` API 而不是 `RxDart` 库,这样会让操作变得非常简单,我们不需要额外的库来实现同样的事情: ->>>>>>> upstream/master ```dart import 'dart:async'; @@ -198,11 +155,7 @@ class Bloc { ## 使用 PublishSubject 代替 BehaviorSubject -<<<<<<< HEAD -对于那些在 Flutter 项目中使用 `RxDart` 库的人来说,这一点会更加地明确。`BehaviorSubject` 是一个特殊的 `StreamController`,它会捕获到已经添加到 controller 的最新项,并且将其作为新的 listener 的第一个事件触发。即使你在 `BehaviorSubject` 上调用 `close()` 或者 `drain()`,它仍然会保留最后一项,并且在这个 listener 被订阅的时候触发。如果开发人员不了解这个功能,那么对于开发人员来说,可以说是一个噩梦。而 `PublishSubject` 不会存储最后一项,更加适合于大多数情况。在这个[项目](https://github.com/SAGARSURI/Goals)中,可以查看 `BehaviorSubject` 的功能。运行应用程序,并且跳转到 'Add Goal' 页面,在表单中输入详细信息,并且导航回来。现在,再次访问 'Add Goal' 页面,你就会发现预先填写了你之前输入的数据的表单。如果你是一个像我一样偷懒的人,那么可以看我下面附上的视频: -======= 对于那些在 Flutter 项目中使用 `RxDart` 库的人来说,这一点会更加地明确。`BehaviorSubject` 是一个特殊的 `StreamController`,它会捕获到已经添加到 controller 的最新项,并且将其作为新的 listener 的第一个事件触发。即使你在 `BehaviorSubject` 上调用 `close()` 或者 `drain()`,它仍然会保留最后一项,并且在这个 listener 被订阅的时候触发。如果开发人员不了解这个功能,这有可能会变成一场噩梦。而 `PublishSubject` 不会存储最后一项,更加适合于大多数情况。在这个[项目](https://github.com/SAGARSURI/Goals)中,可以查看 `BehaviorSubject` 的功能。运行应用程序,并且跳转到 'Add Goal' 页面,在表单中输入详细信息,并且跳转回来。现在,再次访问 'Add Goal' 页面,你就会发现表单里已经预先填写了你之前输入的数据。如果你和我一样懒,那么可以看我下面附上的视频: ->>>>>>> upstream/master [Goals App Demo](https://youtu.be/N7-C3o_O1jE) @@ -245,11 +198,7 @@ class MyApp extends StatelessWidget { ``` -<<<<<<< HEAD -你可以清楚地看到,多个 BLoC Provider 是嵌套的。这时候,你可能会担心,如果继续在同一个链中添加更多的 BLoC,会导致一场噩梦,你可能会得出 BLoC 模式无法扩展的结论。但是,让我告诉你,当你需要在 Widget 树中访问多个 BLoC 的时候,可能会有一种特殊的情况(BLoC 只保存应用程序所需要的 UI 配置),因此,对于这种情况,上述的嵌套是完全正常的。但是我建议你在大多数的情况下,还是要避免这种嵌套的,并且只在实际需要的地方提供 BLoC。因此,比如当你需要导航到新的页面的时候,可以像这样使用 BLoC Provider: -======= 你可以清楚地看到,多个 BLoC Provider 是嵌套的。这时候,那么你一定会担心,如果继续在同一个链中添加更多的 BLoC,会导致一场噩梦,你可能会得出 BLoC 模式无法扩展的结论。但是,让我告诉你,当你需要在 Widget 树中访问多个 BLoC 的时候,可能会有一种特殊的情况(BLoC 只保存应用程序所需要的 UI 配置),因此,对于这种情况,上述的嵌套是完全没问题的。但是我建议你在大多数的情况下,还是要避免这种嵌套的,并且只在实际需要的地方提供 BLoC。因此,比如当你需要导航到新的页面的时候,可以像这样使用 BLoC Provider: ->>>>>>> upstream/master ```dart openDetailPage(ItemModel data, int index) { @@ -272,19 +221,11 @@ openDetailPage(ItemModel data, int index) { } ``` -<<<<<<< HEAD -这样,`MovieDetailBlocProvider` 将会为 `MovieDetail` 页面提供 BLoC,而不是整个组件树。你可以看到,我将 `MovieDetailScreen` 存储在一个新的 `final variable` 中,来避免每次在 `MovieDetailScreen` 中打开或者关闭键盘的时候,都会重新创建 `MovieDetailScreen` 的问题。 - -## 还没有结束 - -虽然这里是本文的结尾了,但并不是这个主题的结尾。随着我学习到更好的扩展和实现模式的方法,我将会不断为这个优化 BLoC 模式的关键点提供新的观点。我希望这些关键点可以帮助你更好地实现 BLoC 模式。Keep learning and keep coding :)。如果你喜欢这篇文章,可以通过点赞来表达你的爱。 -======= 这样,`MovieDetailBlocProvider` 就不会为整个组件树,而是会为 `MovieDetail` 页面提供 BLoC。你可以看到,我将 `MovieDetailScreen` 存储在一个新的 `final variable` 中,来避免每次在 `MovieDetailScreen` 中打开或者关闭键盘的时候,都会重新创建 `MovieDetailScreen` 的问题。 ## 还没有结束 虽然这里是本文的结尾了,但并不是这个主题的结尾。我也会在这个有关优化 BLoC 模式的文集中不断添加新的想法,从而继续丰富它的内容。我希望这些想法可以帮助你更好地实现 BLoC 模式。Keep learning and keep coding :)。如果你喜欢这篇文章,可以通过点赞来表达你的爱。 ->>>>>>> upstream/master 有任何疑问,请在 [LinkedIn](https://www.linkedin.com/in/sagar-suri/) 与我联系,或者在 [Twitter](https://twitter.com/SagarSuri94) 上关注我。我会尽我所能解决你的问题。 From 7276e785a68b7e7b6ab4e12f6603a555921ded2b Mon Sep 17 00:00:00 2001 From: Hao Li Date: Sun, 7 Jul 2019 05:42:28 -0400 Subject: [PATCH 03/68] =?UTF-8?q?=E5=BE=AE=E5=89=8D=E7=AB=AF=EF=BC=9A?= =?UTF-8?q?=E6=9C=AA=E6=9D=A5=E5=89=8D=E7=AB=AF=E5=BC=80=E5=8F=91=E7=9A=84?= =?UTF-8?q?=E6=96=B0=E8=B6=8B=E5=8A=BF=20=E2=80=94=20=E7=AC=AC=E4=BA=8C?= =?UTF-8?q?=E9=83=A8=E5=88=86=20(#6068)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Finish translation * Modify translation in code comments * Fix format and delete original paragraphs * Resolve modification suggestions * Remove an extra empty line * Update micro-frontends-2.md --- TODO1/micro-frontends-2.md | 124 ++++++++++++++++++------------------- 1 file changed, 61 insertions(+), 63 deletions(-) diff --git a/TODO1/micro-frontends-2.md b/TODO1/micro-frontends-2.md index 625c9b5deb2..fa7ac2ab32f 100644 --- a/TODO1/micro-frontends-2.md +++ b/TODO1/micro-frontends-2.md @@ -2,8 +2,8 @@ > * 原文作者:[Cam Jackson](https://camjackson.net/) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/micro-frontends-2.md](https://github.com/xitu/gold-miner/blob/master/TODO1/micro-frontends-2.md) -> * 译者: -> * 校对者: +> * 译者:[lihaobhsfer](https://github.com/lihaobhsfer) +> * 校对者:[动力小车](https://github.com/Stevens1995), [柯小基](https://github.com/lgh757079506) # 微前端:未来前端开发的新趋势 — 第二部分 @@ -16,39 +16,39 @@ > * [微前端:未来前端开发的新趋势 — 第三部分](https://github.com/xitu/gold-miner/blob/master/TODO1/micro-frontends-3.md) > * [微前端:未来前端开发的新趋势 — 第四部分](https://github.com/xitu/gold-miner/blob/master/TODO1/micro-frontends-4.md) -## The example +## 示例 -Imagine a website where customers can order food for delivery. On the surface it's a fairly simple concept, but there's a surprising amount of detail if you want to do it well: +想象一个网页,消费者可以在上面点外卖。表面上看起来这是一个很简单的概念,但是如果想把它做好,有非常多的细节需要考虑。 -* There should be a landing page where customers can browse and search for restaurants. The restaurants should be searchable and filterable by any number of attributes including price, cuisine, or what a customer has ordered previously -* Each restaurant needs its own page that shows its menu items, and allows a customer to choose what they want to eat, with discounts, meal deals, and special requests -* Customers should have a profile page where they can see their order history, track delivery, and customise their payment options +* 应该有一个引导页,消费者可以在这里浏览和搜索餐厅。这些餐厅可以通过任意数量的属性搜索或者过滤,包括价格、菜系或先前订单。 +* 每一个餐厅都需要它自己的页面来显示菜单,并允许消费者选择他们想点什么,并有折扣、套餐、特殊要求这些选项。 +* 消费者应该有一个用户页面来查看订单历史、追踪外卖并自定义支付选项。 -![A wireframe of a food delivery website](https://martinfowler.com/articles/micro-frontends/wireframe.png) +![一个餐饮外卖网站的线框图](https://martinfowler.com/articles/micro-frontends/wireframe.png) -Figure 4: A food delivery website may have several reasonably complex pages +图 4:一个餐饮外卖网页可能会有几个相当复杂的页面。 -There is enough complexity in each page that we could easily justify a dedicated team for each one, and each of those teams should be able to work on their page independently of all the other teams. They should be able to develop, test, deploy, and maintain their code without worrying about conflicts or coordination with other teams. Our customers, however, should still see a single, seamless website. +每个页面都足够复杂到需要一个团队来完成。每个团队都理应能够独立地开发他们负责的页面。他们需要能够开发、测试、部署、维护他们的代码,并无需担心与其他团队的冲突与协调。我们的消费者,看到的仍然应该是一个完整、无缝的网页。 -Throughout the rest of this article, we'll be using this example application wherever we need example code or scenarios. +在文章接下来的部分里,当我们需要示例代码或者情景时,我们将会使用这个应用作为例子。 * * * -## Integration approaches +## 集成方式 -Given the fairly loose definition above, there are many approaches that could reasonably be called micro frontends. In this section we'll show some examples and discuss their tradeoffs. There is a fairly natural architecture that emerges across all of the approaches - generally there is a micro frontend for each page in the application, and there is a single **container application**, which: +根据前文相对宽松的定义,多种方法都能被叫做微前端。在这一节中我们会看一些例子并讨论它们的优劣。这些方法中共有一个相对自然的架构 —— 总体上讲,应用中的每一个页面都有一个微前端,然后还有唯一一个**容器应用**,用于: -* renders common page elements such as headers and footers -* addresses cross-cutting concerns like authentication and navigation -* brings the various micro frontends together onto the page, and tells each micro frontend when and where to render itself +* 渲染公用页面元素,如页眉页脚 +* 解决跨页面的一些需求,如授权和导航 +* 将多个微前端集成到页面上,并告知每个微前端何时在哪渲染自己 -![A web page with boxes drawn around different sections. One box wraps the whole page, labelling it as the 'container application'. Another box wraps the main content (but not the global page title and navigation), labelling it as the 'browse micro frontend'](https://martinfowler.com/articles/micro-frontends/composition.png) +![一个用方框画出不同部分的网页。一个方框包含了整个页面,标记为“容器应用”,另一个方框包括了主要内容(全局页面标题和导航除外),标记为“浏览微前端”](https://martinfowler.com/articles/micro-frontends/composition.png) -Figure 5: You can usually derive your architecture from the visual structure of the page +图 5:你通常可以从页面结构推出你的架构 -### Server-side template composition +### 服务端模板编写 -We start with a decidedly un-novel approach to frontend development - rendering HTML on the server out of multiple templates or fragments. We have an `index.html` which contains any common page elements, and then uses server-side includes to plug in page-specific content from fragment HTML files: +我们从一个很常见的前端开发方法开始 —— 在服务器端基于一些模板和代码片段渲染 HTML 页面。我们有一个 `index.html` 文件,包含所有公用的页面元素,然后我们用服务器端的 `includes` 来加入从 HTML 文件片段提取的页面内容: ``` @@ -63,7 +63,7 @@ We start with a decidedly un-novel approach to frontend development - rendering ``` -We serve this file using Nginx, configuring the `$PAGE` variable by matching against the URL that is being requested: +我们用 Nginx 来提供这个文件,配置 `$PAGE` 变量,将其与请求的 URL 匹配。 ``` server { listen 8080; @@ -73,10 +73,10 @@ server { index index.html; ssi on; - # Redirect / to /browse + # 重定向 / 至 /browse rewrite ^/$ http://localhost:8080/browse redirect; - # Decide which HTML fragment to insert based on the URL + # 根据URL确定要插入哪个 HTML 片段 location /browse { set $PAGE 'browse'; } @@ -87,24 +87,24 @@ server { set $PAGE 'profile' } - # All locations should render through index.html + # 所有位置都应经 index.html 渲染 error_page 404 /index.html; } ``` -This is fairly standard server-side composition. The reason we could justifiably call this micro frontends is that we've split up our code in such a way that each piece represents a self-contained domain concept that can be delivered by an independent team. What's not shown here is how those various HTML files end up on the web server, but the assumption is that they each have their own deployment pipeline, which allows us to deploy changes to one page without affecting or thinking about any other page. +这是一个相对标准的服务端组合。我们能够将其称为微前端的原因是,我们将代码分离,这样每一部分代码都是一个自我包含的领域概念,并能够被一个独立的团队开发。我们没有看到的是,这些不同的 HTML 文件最后如何到了服务器端,但是我们假设每一个页面都有它们自己的部署流程,允许我们对一个页面部署修改,同时不影响或者无需考虑其他页面。 -For even greater independence, there could be a separate server responsible for rendering and serving each micro frontend, with one server out the front that makes requests to the others. With careful caching of responses, this could be done without impacting latency. +对于更大的独立性,每一个微前端都可以由独立的服务器来负责渲染,并由一个服务器负责向剩下的发送请求。使用精心设计的缓存来存储响应,这种实施方案不会影响延迟。 -![A flow diagram showing a browser making a request to a 'container app server', which then makes requests to one of either a 'browse micro frontend server' or a 'order micro frontend server'](https://martinfowler.com/articles/micro-frontends/ssi.png) +![一个流程图,展示浏览器向“容器应用服务器”发送请求,该服务器随后向“浏览微前端服务器”或“订单微前端服务器”发送请求](https://martinfowler.com/articles/micro-frontends/ssi.png) -Figure 6: Each of these servers can be built and deployed to independently +图 6:每一个服务器都可以独立构建和部署 -This example shows how micro frontends is not necessarily a new technique, and does not have to be complicated. As long as we're careful about how our design decisions affect the autonomy of our codebases and our teams, we can achieve many of the same benefits regardless of our tech stack. +这个例子展示为何微前端不是一个新技术,并且不需要很复杂。只要我们仔细考虑我们的设计决定如何影响代码库和团队的自治,我们就能获取同样多的便利,无论我们的技术栈是什么。 -### Build-time integration +### 构建时集成 -One approach that we sometimes see is to publish each micro frontend as a package, and have the container application include them all as library dependencies. Here is how the container's `package.json` might look for our example app: +我们有时会看到一种方法,即以一个包来发布每一个微前端,然后由容器应用引入这些包作为库依赖。我们示例应用的容器的 `package.json` 可能是这样: ``` { @@ -119,13 +119,13 @@ One approach that we sometimes see is to publish each micro frontend as a packag } ``` -At first this seems to make sense. It produces a single deployable Javascript bundle, as is usual, allowing us to de-duplicate common dependencies from our various applications. However, this approach means that we have to re-compile and release every single micro frontend in order to release a change to any individual part of the product. Just as with microservices, we've seen enough pain caused by such a **lockstep release process** that we would recommend strongly against this kind of approach to micro frontends. +乍一看这可能有道理。它产出单个可部署的 JavaScript 包,和往常一样,允许我们从我们多样的应用中解耦公用依赖。然而,这个方法意味着,为了在产品任意一个部分发布修改,我们必须重新编译和发布每一个微前端。如同微服务一样,我们已经体会过了这种**因循守旧的发布流程**带来的痛苦,以至于我们强烈反对在微前端使用同样的方法。 -Having gone to all of the trouble of dividing our application into discrete codebases that can be developed and tested independently, let's not re-introduce all of that coupling at the release stage. We should find a way to integrate our micro frontends at run-time, rather than at build-time. +踩过了将应用分为离散的、可独立开发测试的代码库带来的所有的坑,我们就不再介绍发布阶段的耦合问题了。我们需要找到一个在运行时集成微前端的方法,而非构造时方法。 -### Run-time integration via iframes +### 通过 iframes 运行时集成 -One of the simplest approaches to composing applications together in the browser is the humble iframe. By their nature, iframes make it easy to build a page out of independent sub-pages. They also offer a good degree of isolation in terms of styling and global variables not interfering with each other. +将应用组合到浏览器的一个最简便的方法便是使用 iframe。其特性让使用独立的子页面构建一个页面变得简单。它也提供了一个不错的分离性,包括样式和全局变量互不干扰。 ``` @@ -151,13 +151,13 @@ One of the simplest approaches to composing applications together in the browser ``` -Just as with the [server-side includes option](#Server-sideTemplateComposition), building a page out of iframes is not a new technique and perhaps does not seem that exciting. But if we revisit the chief benefits of micro frontends [listed earlier](https://github.com/xitu/gold-miner/blob/master/TODO1/micro-frontends-1.md#%E4%BC%98%E7%82%B9), iframes mostly fit the bill, as long as we're careful about how we slice up the application and structure our teams. +和[服务端的引入选项](#服务端模板编写)一样,用 iframes 构建页面不是一个新的技术,而且可能不是很令人兴奋。但如果我们重温[之前提过](https://github.com/xitu/gold-miner/blob/master/TODO1/micro-frontends-1.md#%E4%BC%98%E7%82%B9)的微前端的好处,iframes 几乎都有,只要我们仔细考虑如何将应用分成独立部分、如何构建团队。 -We often see a lot of reluctance to choose iframes. While some of that reluctance does seem to be driven by a gut feel that iframes are a bit “yuck”, there are some good reasons that people avoid them. The easy isolation mentioned above does tend to make them less flexible than other options. It can be difficult to build integrations between different parts of the application, so they make routing, history, and deep-linking more complicated, and they present some extra challenges to making your page fully responsive. +我们经常看到很多人不愿意选择 iframes。虽然部分原因似乎是直觉感觉 iframe 有点“糟糕”,但人们也有很好的理由不使用它们。上面提到的简单隔离确实会使它们比其他选项更不灵活。在应用程序的不同部分之间构建集成可能很困难,因此它们使路由,历史记录和深层链接变得更加复杂,并且它们对使页面完全响应性提出了一些额外的挑战。 -### Run-time integration via JavaScript +### 通过 JavaScript 运行时集成 -The next approach that we'll describe is probably the most flexible one, and the one that we see teams adopting most frequently. Each micro frontend is included onto the page using a ` @@ -176,7 +176,7 @@ The next approach that we'll describe is probably the most flexible one, and the
``` -The above is obviously a primitive example, but it demonstrates the basic technique. Unlike with build-time integration, we can deploy each of the `bundle.js` files independently. And unlike with iframes, we have full flexibility to build integrations between our micro frontends however we like. We could extend the above code in many ways, for example to only download each JavaScript bundle as needed, or to pass data in and out when rendering a micro frontend. +以上显然是一个比较初始的例子,但它演示了基本技术。与构建时集成不同,我们可以独立部署每个 `bundle.js` 文件。与 iframe 不同,我们有充分的灵活性来以我们偏好的方式构建微前端之间的集成。我们可以通过多种方式扩展上述代码,例如,只根据需要下载每个 JavaScript 包,或者在呈现微前端时传入和传出数据。 -The flexibility of this approach, combined with the independent deployability, makes it our default choice, and the one that we've seen in the wild most often. We'll explore it in more detail when we get into the [full example.](#TheExampleInDetail) +这一方法的灵活性,与独立部署性结合,使它成为了我们的默认选择,并且是最为常见的一种选择。当我们到了[完整示例](https://github.com/xitu/gold-miner/blob/master/TODO1/micro-frontends-2.md#案例详解)时我们将会探索这方面的更多细节。 -### Run-time integration via Web Components +### 通过网页组件运行时集成 -One variation to the previous approach is for each micro frontend to define an HTML custom element for the container to instantiate, instead of defining a global function for the container to call. +前面这种方法的一个变种就是,对于每一个微前端,定义一个 HTML 自定义元素让容器来构建,而非定义一个全局函数来让容器调用。 ``` @@ -208,8 +207,8 @@ One variation to the previous approach is for each micro frontend to define an H

Welcome to Feed me!

- - + + @@ -217,7 +216,7 @@ One variation to the previous approach is for each micro frontend to define an H
``` -There are a number of perceived benefits to doing this, but my aim later in this article is to either debunk these claims, or show how other costs vastly outweigh them. +这么做有许多显而易见的好处,但是我这篇文章的目的是驳倒这些说法,或者是表明这么做成本远大于收益。 -* **It’s convenient.** It requires very little effort or brainpower to include files like this. Copy and paste a line of HTML and you’re done. Easy. -* **We get access to a CDN.** `code.jquery.com` is served by [StackPath](https://www.stackpath.com/products/cdn/), a CDN. By linking to assets on this origin, we get CDN-quality delivery, free! -* **Users might already have the file cached.** If `website-a.com` links to `https://code.jquery.com/jquery-3.3.1.slim.min.js`, and a user goes from there to `website-b.com` who also links to `https://code.jquery.com/jquery-3.3.1.slim.min.js`, then the user will already have that file in their cache. +* **方便**。像这样链入文件特别省事,复制粘贴一行 HTML,搞定。真简单。 +* **我们接入了 CDN**。`code.jquery.com` 是 [StackPath](https://www.stackpath.com/products/cdn/) 这个 CDN 来服务的。我们这样链接资源可以得到 CDN 级别的传输质量,还是免费的! +* **用户可能已经将文件缓存好**。如果 `website-a.com` 链接到 `https://code.jquery.com/jquery-3.3.1.slim.min.js`,然后用户从那跳转到恰好也链接到 `https://code.jquery.com/jquery-3.3.1.slim.min.js` 的 `website-b.com`,那么文件就已经在用户的缓存里了。 -## Risk: Slowdowns and Outages +## 风险:减速和宕机 -I won’t go into too much detail in this post, because I have a [whole article](https://csswizardry.com/2017/07/performance-and-resilience-stress-testing-third-parties/) on the subject of third party resilience and the risks associated with slowdowns and outages. Suffice to say, if you have any critical assets served by third party providers, and that provider is suffering slowdowns or, heaven forbid, outages, it’s pretty bleak news for you. You’re going to suffer, too. +这篇文章里我不会讲得太详细,因为我写了一篇[完整的文章](https://csswizardry.com/2017/07/performance-and-resilience-stress-testing-third-parties/)来讨论第三方服务的恢复力以及相关的减速与宕机风险。可以这样说,如果你有任何重要资源放在第三方服务器上,一旦服务商出现阻塞,甚至直接宕机,那么就糟糕了。你也会遭殃。 -If you have any render-blocking CSS or synchronous JS hosted on third party domains, go and bring it onto your own infrastructure **right now**. Critical assets are far too valuable to leave on someone else’s servers. +如果你用第三方域托管阻塞渲染的 CSS 或同步 JS,**现在**就把它移回自己的基础设施上。重要的资源不应该放在别人的服务器上。 -## Risk: Service Shutdowns +## 风险:服务停止 -A far less common occurrence, but what happens if a provider decides they need to shut down the service? This is exactly what [Rawgit](https://rawgit.com) did in October 2018, yet (at the time of writing) a crude GitHub code search still yielded [over a million references](https://github.com/search?q=rawgit&type=Code) to the now-sunset service, and almost 20,000 live sites are still linking to it! +虽然并不常见,但是如果服务商决定要停止服务怎么办呢?2018 年十月 [Rawgit](https://rawgit.com) 关站,然而(在本文写成时)粗略的 Github 代码检索得出,[至少一百万个](https://github.com/search?q=rawgit&type=Code)这项已经停止服务的引用,大概 20,000 个正常运行的网站仍在使用它! ![](https://csswizardry.com/wp-content/uploads/2019/05/big-query-rawgit.jpg) -Many thanks to [Paul Calvano](https://twitter.com/paulcalvano) who very kindly [queried the HTTPArchive](https://bigquery.cloud.google.com/savedquery/226352634162:7c27aa5bac804a6687f58db792c021ee) for me. +十分感谢 [Paul Calvano](https://twitter.com/paulcalvano) 为我提供了 [HTTPArchive 上的检索结果](https://bigquery.cloud.google.com/savedquery/226352634162:7c27aa5bac804a6687f58db792c021ee)。 -## Risk: Security Vulnerabilities +## 风险:安全隐患 -Another thing to take into consideration is the simple question of trust. If we’re bringing content from external sources onto our page, we have to hope that the assets that arrive are the ones we were expecting them to be, and that they’re doing only what we expected them to do. +另外一个需要考虑的问题是可信度。如果我们将外源内容放在我们的页面上,我们就会希望送达的资源是我们所期望的,而且只会发挥我们所期望的作用。 -Imagine the damage that would be caused if someone managed to take control of a provider such as `code.jquery.com` and began serving compromised or malicious payloads. It doesn’t bear thinking about! +想象一下如果有人控制了 `code.jquery.com` 这种服务商并开始提供有漏洞的或恶意的 payload, 那样会造成多大的损失。想都不敢想! -### Mitigation: Subresource Integrity +### 缓解方案:子资源完整性 -To the credit of all of the providers referenced so far in this article, they do all make use of [Subresource Integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity) (SRI). SRI is a mechanism by which the provider supplies a hash (technically, a hash that is then Base64 encoded) of the exact file that you both expect and intend to use. The browser can then check that the file you received is indeed the one you requested. +本文提到的所有服务商值得称道的一点是,它们都应用了[子资源完整性](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity) (SRI)。SRI 的机制通过服务商为双方期待使用的文件提供哈希值(准确得讲 Base64 编码的哈希值)来实现。浏览器会检查你收到的文件正是你所请求的那一个。 ```html ``` -Again, if you absolutely must link to an externally hosted static asset, make sure it’s SRI-enabled. You can add SRI yourself using [this handy generator](https://www.srihash.org/). +重申一下,如果你绝对必须链接到外部托管的静态资源,那就确保它实现了 SRI。你可以用这个[好用的生成器](https://www.srihash.org/)来自己添加 SRI。 -## Penalty: Network Negotiation +## 扣分项:网络协商 -One of the biggest and most immediate penalties we pay is the cost of opening new TCP connections. Every new origin we need to visit needs a connection opening, and that can be very costly: DNS resolution, TCP handshakes, and TLS negotiation all add up, and the story gets worse the higher the latency of the connection is. +一个最重要最直接的扣分项就是降低新 TCP 连接的成本。我们要访问的每个新节点都需要打开连接,这些步骤的消耗非常大:DNS 解析,TCP 握手,TLS 协商,而且一旦连接的延迟提高就会让情况更糟。 -I’m going to use an example taken straight from Bootstrap’s own [Getting Started](https://getbootstrap.com/docs/4.3/getting-started/introduction/). They instruct users to include these following four files: +我会拿 Bootstrap 的[入门](https://getbootstrap.com/docs/4.3/getting-started/introduction/)当做例子。他们指导用户引入以下四个文件: ```html @@ -68,37 +68,37 @@ I’m going to use an example taken straight from Bootstrap’s own [Getting Sta ``` -These four files are hosted across three different origins, so we’re going to need to open three TCP connections. How much does that cost? +这四个文件由三个不同的源来托管,所以我们需要打开三个 TCP 连接。成本是多少呢? -Well, on a reasonably fast connection, hosting these static assets off-site is 311ms, or 1.65×, slower than hosting them ourselves. +好吧,在还不错的网速上,托管这些静态资源用掉 311ms,或 1.65 倍慢于放置在自己主机上。 ![](https://csswizardry.com/wp-content/uploads/2019/05/wpt-off-site-cable.png) -By linking to three different origins in order to serve static assets, we cumulatively lose a needless 805ms to network negotiation. [Full test.](https://www.webpagetest.org/result/190531_FY_618f9076491312ef625cf2b1a51167ae/3/details/) +连接到托管静态资源的三个不同的源,我们在网络协商上总共花费了多余的 805ms。[完整测试在此。](https://www.webpagetest.org/result/190531_FY_618f9076491312ef625cf2b1a51167ae/3/details/) -Okay, so not exactly terrifying, but Trainline, a client of mine, found that by reducing latency by 300ms, [customers spent an extra £8m a year](https://wpostats.com/2016/05/04/trainline-spending.html). This is a pretty quick way to make eight mill. +OK,不是很糟,但是我的一个客户 Trainline 发现为了降低 300ms 延迟,[客户们每年要多花费 800 万英镑](https://wpostats.com/2016/05/04/trainline-spending.html)。这么花掉 800 万真是浪费。 ![](https://csswizardry.com/wp-content/uploads/2019/05/wpt-self-hosted-cable.png) -By simply moving our assets onto the host domain, we completely remove any extra connection overhead. [Full test.](https://www.webpagetest.org/result/190531_FX_f7d7b8ae511b02aabc7fa0bbef0e37bc/3/details/) +单单把资源移到主域,我们就可以完全去除多余的连接开支。[全文](https://www.webpagetest.org/result/190531_FX_f7d7b8ae511b02aabc7fa0bbef0e37bc/3/details/) -On a slower, higher-latency connection, the story is much, much worse. Over 3G, the externally-hosted version comes in at an eye-watering **1.765s slower**. I thought this was meant to make our site faster?! +在高延迟的连接上,情况会糟得多。3G 网络上,外部托管的版本要多花 1.765s 😭,这么做本来不是为了让网站更快吗?! ![](https://csswizardry.com/wp-content/uploads/2019/05/wpt-off-site-3g.png) -On a high latency connection, network overhead totals a whopping 5.037s. All completely avoidable. [Full test.](https://www.webpagetest.org/result/190531_XE_a95eebddd2346f8bb572cecf4a8dae68/3/details/) +在高延迟连接上,总联网开支竟达到 5.037s。这完全可以避免的。[全文](https://www.webpagetest.org/result/190531_XE_a95eebddd2346f8bb572cecf4a8dae68/3/details/) -Moving the assets onto our own infrastructure brings load times down from around 5.4s to just 3.6s. +将资源移到自己的基础设施上会将加载时间从大约 5.4s 降到仅仅 3.6s。 ![](https://csswizardry.com/wp-content/uploads/2019/05/wpt-self-hosted-3g.png) -By self-hosting our static assets, we don’t need to open any more connections. [Full test.](https://www.webpagetest.org/result/190531_ZF_4d76740567ec1eba1e6ec67acfd57627/1/details/) +自托管静态资源时,我们无需打开更多连接。[全文](https://www.webpagetest.org/result/190531_ZF_4d76740567ec1eba1e6ec67acfd57627/1/details/) -If this isn’t already a compelling enough reason to self-host your static assets, I’m not sure what is! +如果这还不够说服你自托管静态资源,我也没办法了! -### Mitigation: `preconnect` +### 缓解方案:`preconnect` -Naturally, my whole point here is that you should not host any static assets off-site if you’re otherwise able to self-host them. However, if your hands are somehow tied, then you can use [a `preconnect` Resource Hint](https://speakerdeck.com/csswizardry/more-than-you-ever-wanted-to-know-about-resource-hints?slide=28) to preemptively open a TCP connection to the specified origin(s): +很自然的,我的主要观点是如果你能自己 host 静态资源你就不应该托管它们。但是,如果这样做不方便,你就可以用 [`preconnect` 资源提示](https://speakerdeck.com/csswizardry/more-than-you-ever-wanted-to-know-about-resource-hints?slide=28)来提前打开相应源的 TCP 连接: ```html @@ -112,65 +112,65 @@ Naturally, my whole point here is that you should not host any static assets off ``` -For bonus points, deploying these as [HTTP headers](https://andydavies.me/blog/2019/03/22/improving-perceived-performance-with-a-link-rel-equals-preconnect-http-header/) will be even faster. +把它们当作 [HTTP headers](https://andydavies.me/blog/2019/03/22/improving-perceived-performance-with-a-link-rel-equals-preconnect-http-header/) 来部署会更好。 -**N.B.** Even if you do implement `preconnect`, you’re still only going to make a small dent in your lost time: you still need to open the relevant connections, and, especially on high latency connections, it’s unlikely that you’re ever going to fully pay off the overhead upfront. +**注意** 即使你实现了 `preconnect`,你也只能挽回一小部分浪费掉的时间:你还是要打开相关连接,特别是那些高延迟的,你不太可能马上把所有的开支抵消掉。 -## Penalty: Loss of Prioritisation +## 扣分项:优先处理策略带来的损失 -The second penalty comes in the form of a protocol-level optimisation that we miss out on the moment we split content across domains. If you’re running over HTTP/2—which, by now, you should be—you get access to prioritisation. All streams (ergo, resources) within the same TCP connection carry a priority, and the browser and server work in tandem to build a dependency tree of all of these prioritised streams so that we can return critical assets sooner, and perhaps delay the delivery of less important ones. +第二个扣分项以协议层优先处理的形式存在,而这种优先处理在我们将内容跨域存放是被破坏了。如果你用 HTTP/2(你确实应该用),你就会用到优先处理。同一个 TCP 连接上的所有的流(也就是说资源)都有一个优先级,而浏览器和服务器会协作建立这些优先处理流的依赖树,从而优先递送关键资源,延迟递送不太重要的资源。 -To fully understand the benefits of prioritisation, [Pat Meenan’s post](https://calendar.perfplanet.com/2018/http2-prioritization/) on the topic serves as a good primer. +想要完全理解优先处理的好处,[Pat Meenan 的文章](https://calendar.perfplanet.com/2018/http2-prioritization/)很好的帮助你入门。 -**N.B.** Technically, owing to H/2’s [connection coalescence](https://daniel.haxx.se/blog/2016/08/18/http2-connection-coalescing/), requests can be prioritised against each other over different domains as long as they share the same IP address. +**注意** 从技术角度讲,由于 HTTP/2 的[连接合并](https://daniel.haxx.se/blog/2016/08/18/http2-connection-coalescing/),只要有相同的 IP 地址,不同域上的请求就会被依优先级处理。 -If we split our assets across multiple domains, we have to open up several unique TCP connections. We cannot cross-reference any of the priorities within these connections, so we lose the ability to deliver assets in a considered and well designed manner. +如果我们把资源分置到多个域上,我们就要打开好几个不同的 TCP 连接。我们没法在不同连接上相互引用那些优先级,所以就会失去以这种深思熟虑、妥善设计的方式传递资源的能力。 -Compare the two HTTP/2 dependency trees for both the off-site and self-hosted versions respectively: +比较一下托管和自托管两个版本的 HTTP/2 依赖树: ![](https://csswizardry.com/wp-content/uploads/2019/05/wpt-dep-tree-off-site.png) -Notice how we need to build new dependency trees per origin? Stream IDs 1 and 3 keep reoccurring. +注意到我们要对每个源建立不同的依赖树了吗?Stream ID 1 和 3 反复出现。 ![](https://csswizardry.com/wp-content/uploads/2019/05/wpt-dep-tree-self-hosted.png) -By hosting all content under the same origin, we can build one, more complete dependency tree. Every stream has a unique ID as they’re all in the same tree. +把所有内容放在同一个源下,我们就可以建立一个唯一的、完整的依赖树。因为所有流都在同一个树里,它们都有唯一的 ID。 -Fun fact: Stream IDs with an odd number were initiated by the client; those with an even number were initiated by the server. I honestly don’t think I’ve ever seen an even-numbered ID in the wild. +有趣的是,奇数 Stream ID 是从客户端起始的,偶数的是被服务器起始的。老实说我从来没见过一个偶数 ID。 -If we serve as much content as possible from one domain, we can let H/2 do its thing and prioritise assets more completely in the hopes of better-timed responses. +如果我们尽可能从一个域提供很多内容,我们可以让 HTTP/2 更全面的做好优先处理,以期更迅捷的响应。 -## Penalty: Caching +## 扣分项:缓存 -By and large, static asset hosts seem to do pretty well at establishing long-lived `max-age` directives. This makes sense, as static assets at versioned URLs (as above) will never change. This makes it very safe and sensible to enforce a reasonably aggressive cache policy. +大致上说,静态资源主机很适用于建立长期 `max-age` 指令。这很自然,因为版本化 URL 上的静态资源(如上)从来不会变。因此使用适度激进的缓存策略是安全合理的。 -That said, this isn’t always the case, and by self-hosting your assets you can design [much more bespoke caching strategies](https://csswizardry.com/2019/03/cache-control-for-civilians/). +话虽这么讲,也不是所有情况都适用,而且用自托管资源你可以设计出[更有针对性的的缓存策略](https://csswizardry.com/2019/03/cache-control-for-civilians/)。 -## Myth: Cross-Domain Caching +## 神话:跨域缓存 -A more interesting take is the power of cross-domain caching of assets. That is to say, if lots and lots of sites link to the same CDN-hosted version of, say, jQuery, then surely users are likely to already have that exact file on their machine already? Kinda like peer-to-peer resource sharing. This is one of the most common arguments I hear in favour of using a third-party static asset provider. +一种更有趣的看法是关于资源跨域缓存的威力的。意思是,如果许多网站链接到同一个 CDN 托管的资源,比如,jQuery,那么用户一定更可能已经在他们的终端上存有相同的文件吗?有点像点对点资源共享。这是支持使用第三方静态资源服务商的最常见理由之一。 -Unfortunately, there seems to be no published evidence that backs up these claims: there is nothing to suggest that this is indeed the case. Conversely, [recent research](https://discuss.httparchive.org/t/analyzing-resource-age-by-content-type/1659) by [Paul Calvano](https://twitter.com/paulcalvano) hints that the opposite might be the case: +不幸的是,似乎没有任何公开证据来支持这些说法:不能证明事实是这样的。相反的,[Paul Calvano](https://twitter.com/paulcalvano) 的[最新研究](https://discuss.httparchive.org/t/analyzing-resource-age-by-content-type/1659)暗示了相反的情况: -> There is a significant gap in the 1st vs 3rd party resource age of CSS and web fonts. 95% of first party fonts are older than 1 week compared to 50% of 3rd party fonts which are less than 1 week old! This makes a strong case for self hosting web fonts! +> 自托管及第三方托管的 CSS 和网络字体的资源寿命有显著不同。95% 的自托管字体久于一周而 50% 的第三方字体不及一周。这是对自托管网络字体的强烈支持! -In general, third party content seems to be less-well cached than first party content. +总体上说,第三方内容不如自托管内容缓存比例高。 -Even more importantly, [Safari has completely disabled this feature](https://andydavies.me/blog/2018/09/06/safari-caching-and-3rd-party-resources/) for fear of abuse where privacy is concerned, so the shared cache technique cannot work for, at the time of writing, [16% of users worldwide](http://gs.statcounter.com/). +更重要的是,[Safari 完全去除了这个功能](https://andydavies.me/blog/2018/09/06/safari-caching-and-3rd-party-resources/)来避免隐私滥用,所以本文写成时共享缓存技术对[世界上 16% 的用户](http://gs.statcounter.com/)是不能应用的。 -In short, although nice in theory, there is no evidence that cross-domain caching is in any way effective. +一句话,虽然理论上很美好,但是无证据表明跨域缓存是有效的。 -## Myth: Access to a CDN +## 神话:访问 CDN -Another commonly touted benefit of using a static asset provider is that they’re likely to be running beefy infrastructure with CDN capabilities: globally distributed, scalable, low-latency, high availability. +另外一个经常被吹捧的静态资源服务的优点在于,它们想必在具有 CDN 能力的优质基础实施上运行的:全球分布,可伸缩,低延迟,高可用度。 -While this is absolutely true, if you care about performance, you should be running your own content from a CDN already. With the price of modern hosting solutions being what they are (this site is fronted by Cloudflare which is free), there’s very little excuse for not serving your own assets from one. +虽然说得没错,但是如果你注重性能,你应该已经用 CDN 运行你的内容了。考虑当代托管服务的价格(本网站是用免费的 Cloudflare),不用 CDN 托管资源实在说不过去。 -Put another way: if you think you need a CDN for your jQuery, you’ll need a CDN for everything. Go and get one. +这么说吧:如果你觉得你的 jQuery 需要用 CDN,那你所用的东西都需要 CDN。用吧。 -## Self-Host Your Static Assets +## 自托管资源 -There really is very little reason to leave your static assets on anyone else’s infrastructure. The perceived benefits are often a myth, and even if they weren’t, the trade-offs simply aren’t worth it. Loading assets from multiple origins is demonstrably slower. Take ten minutes over the next few days to audit your projects, and fetch any off-site static assets under your own control. +实在是没理由把静态资源放在别人的基础设施上。直觉上的优点经常是谎言,即使不是,权衡之后往往就不值得了。从多个源加载资源确实很慢。接下来几天里,花上十分钟来审计一下自己的项目,重新掌控你 off-site 的静态资源吧。 > 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 From a962c8788183933fa47ca08b535f44b4d7d93630 Mon Sep 17 00:00:00 2001 From: xilihuasi <2857818553@qq.com> Date: Wed, 10 Jul 2019 11:11:56 +0800 Subject: [PATCH 17/68] =?UTF-8?q?=E5=BE=AE=E5=89=8D=E7=AB=AF=EF=BC=9A?= =?UTF-8?q?=E6=9C=AA=E6=9D=A5=E5=89=8D=E7=AB=AF=E5=BC=80=E5=8F=91=E7=9A=84?= =?UTF-8?q?=E6=96=B0=E8=B6=8B=E5=8A=BF=20=E2=80=94=20=E7=AC=AC=E4=B8=89?= =?UTF-8?q?=E9=83=A8=E5=88=86=20(#6067)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update micro-frontends-3.md 完成翻译 * Update micro-frontends-3.md 根据校对者意见修改完成 * Update micro-frontends-3.md --- TODO1/micro-frontends-3.md | 88 +++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/TODO1/micro-frontends-3.md b/TODO1/micro-frontends-3.md index a83e8903029..a29b7544ed0 100644 --- a/TODO1/micro-frontends-3.md +++ b/TODO1/micro-frontends-3.md @@ -2,8 +2,8 @@ > * 原文作者:[Cam Jackson](https://camjackson.net/) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/micro-frontends-3.md](https://github.com/xitu/gold-miner/blob/master/TODO1/micro-frontends-3.md) -> * 译者: -> * 校对者: +> * 译者:[xilihuasi](https://github.com/xilihuasi) +> * 校对者:[Stevens1995](https://github.com/Stevens1995), [lgh757079506](https://github.com/lgh757079506) # 微前端:未来前端开发的新趋势 — 第三部分 @@ -16,59 +16,59 @@ > * [微前端:未来前端开发的新趋势 — 第三部分](https://github.com/xitu/gold-miner/blob/master/TODO1/micro-frontends-3.md) > * [微前端:未来前端开发的新趋势 — 第四部分](https://github.com/xitu/gold-miner/blob/master/TODO1/micro-frontends-4.md) -## Cross-application communication +## 跨应用通信 -One of the most common questions regarding micro frontends is how to let them talk to each other. In general, we recommend having them communicate as little as possible, as it often reintroduces the sort of inappropriate coupling that we're seeking to avoid in the first place. +关于微前端最常见的问题就是如何让它们互相交流。一般来说,我们建议通信越少越好,因为这通常会重新引入不恰当的耦合,而这种耦合是我们首先想要避免的。 -That said, some level of cross-app communication is often needed. [Custom events](https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events) allow micro frontends to communicate indirectly, which is a good way to minimise direct coupling, though it does make it harder to determine and enforce the contract that exists between micro frontends. Alternatively, the React model of passing callbacks and data downwards (in this case downwards from the container application to the micro frontends) is also a good solution that makes the contract more explicit. A third alternative is to use the address bar as a communication mechanism, which we'll explore [in more detail later](https://martinfowler.com/articles/micro-frontends.html#Cross-applicationCommunicationViaRouting). +也就是说,某种程度上的通信通常是需要的。[自定义事件]((https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events))允许微前端直接通信,这是最小化直接耦合的好方法,虽然它确实使确定和执行微前端之间存在的约定变得更加困难。另外,向下传递回调和数据(在这里是从容器应用向下到微前端)的 React 模型也是一种好方法,它使得约定更加明确。第三种方法是使用地址栏作为通信机制,后面我们会谈到[更多细节](https://martinfowler.com/articles/micro-frontends.html#Cross-applicationCommunicationViaRouting)。 -> If you are using redux, the usual approach is to have a single, global, shared store for the entire application. However, if each micro frontend is supposed to be its own self-contained application, then it makes sense for each one to have its own redux store. The redux docs even mention ["isolating a Redux app as a component in a bigger application"](https://redux.js.org/faq/store-setup#can-or-should-i-create-multiple-stores-can-i-import-my-store-directly-and-use-it-in-components-myself) as a valid reason to have multiple stores. +> 如果你在使用 redux,常用的方法是建立一个对于整个应用单一、全局、共享的 store。然而,如果微前端都应该是它自己的独立应用,那么它们每个都有自己的 redux store 是有意义的。Redux 文档甚至提到[“将 Redux 应用程序隔离为更大应用程序中的组件”](https://redux.js.org/faq/store-setup#can-or-should-i-create-multiple-stores-can-i-import-my-store-directly-and-use-it-in-components-myself)作为拥有多个 store 的正当理由。 -Whatever approach we choose, we want our micro frontends to communicate by sending messages or events to each other, and avoid having any shared state. Just like sharing a database across microservices, as soon as we share our data structures and domain models, we create massive amounts of coupling, and it becomes extremely difficult to make changes. +无论选择哪种方式,我们希望微前端通过发送消息或者事件来彼此通信,避免任何状态共享。就像跨微服务共享数据库,只要我们共享数据结构和领域模型,就会产生大量的耦合,这会变得非常难以维护。 -As with styling, there are several different approaches that can work well here. The most important thing is to think long and hard about what sort of coupling you're introducing, and how you'll maintain that contract over time. Just as with integration between microservices, you won't be able to make breaking changes to your integrations without having a coordinated upgrade process across different applications and teams. +与样式一样,有几种不同的方法可以在这方面起到很好的作用。最重要的事情是对你正在引入的耦合考虑深远,以及你将如何保持约定。就像微服务之间的集成一样,如果没有跨不同应用程序和团队的协调升级过程,你就无法对集成做出重大变更。 -You should also think about how you'll automatically verify that the integration does not break. Functional testing is one approach, but we prefer to limit the number of functional tests we write due to the cost of implementing and maintaining them. Alternatively you could implement some form of [consumer-driven contracts](https://martinfowler.com/articles/consumerDrivenContracts.html), so that each micro frontend can specify what it requires of other micro frontends, without needing to actually integrate and run them all in a browser together. +你也应该考虑如何自动验证集成没有挂掉。功能测试是一种方式,但我们更希望限制编写功能测试的数量,以便控制实现和维护它们的成本。或者你可以实施某种形式的[消费者驱动的约定](https://martinfowler.com/articles/consumerDrivenContracts.html),这样每个微前端可以指定它对于其他微前端的依赖,无需在浏览器中实际集成和运行它们。 * * * -## Backend communication +## 后端通信 -If we have separate teams working independently on frontend applications, what about backend development? We believe strongly in the value of full-stack teams, who own their application's development from visual code all the way through to API development, and database and infrastructure code. One pattern that helps here is the [BFF](https://samnewman.io/patterns/architectural/bff/) pattern, where each frontend application has a corresponding backend whose purpose is solely to serve the needs of that frontend. While the BFF pattern might originally have meant dedicated backends for each frontend channel (web, mobile, etc), it can easily be extended to mean a backend for each micro frontend. +如果我们有独立的团队在前端应用程序上独立工作,那么后端开发呢?我们非常相信全栈团队的价值,他们从可视化代码到 API 开发、数据库和基础架构代码负责整个应用的开发。[BFF](https://samnewman.io/patterns/architectural/bff/) 模式在这里发挥了作用,每一个前端应用都有一个对应的后端来单独满足前端的需求。虽然 BFF 模式最初可能意味着每个前端通道(web、mobile 等)的专用后端,它可以很容易地扩展为每一个微前端的后端。 -There are a lot of variables to account for here. The BFF might be self contained with its own business logic and database, or it might just be an aggregator of downstream services. If there are downstream services, it may or may not make sense for the team that owns the micro frontend and its BFF, to also own some of those services. If the micro frontend has only one API that it talks to, and that API is fairly stable, then there may not be much value in building a BFF at all. The guiding principle here is that the team building a particular micro frontend shouldn't have to wait for other teams to build things for them. So if every new feature added to a micro frontend also requires backend changes, that's a strong case for a BFF, owned by the same team. +这里有很多因素需要考虑。BFF 可能是自包含的,具有自己的业务逻辑和数据库,或者也可能只是一个下游服务的聚合器。如果有下游服务,那么拥有微前端及其 BFF 的团队可能有或可能没有意义,来拥有一些这样的服务。如果微前端只有一个与之通讯的 API ,并且它相当稳定,那么构建 BFF 就根本没有多大价值了。有一个指导原则是,团队构建一个特定微前端时不应该必须得等其他团队来为他们构建东西。因此如果每当给微前端添加新功能时也要求后端更改,那么由同一个团队拥有的 BFF 就是一个很好的案例。 -![A diagram showing three pairs of frontends / backends. The first backend talks only to its own database. The other two backends talk to shared downstream services. Both approaches are valid.](https://martinfowler.com/articles/micro-frontends/bff.png) +![该图表显示三对前端/后端。第一个后端只与自己的数据库对话。其他两个后端与共享下游服务进行通信。两种方法都是有效的](https://martinfowler.com/articles/micro-frontends/bff.png) -Figure 7: There are many different ways to structure your frontend/backend relationships +图 7:有很多不同的方式构建你的前/后端关系 -Another common question is, how should the user of a micro frontend application be authenticated and authorised with the server? Obviously our customers should only have to authenticate themselves once, so auth usually falls firmly in the category of cross-cutting concerns that should be owned by the container application. The container probably has some sort of login form, through which we obtain some sort of token. That token would be owned by the container, and can be injected into each micro frontend on initialisation. Finally, the micro frontend can send the token with any request that it makes to the server, and the server can do whatever validation is required. +其他常见问题有,应该如何通过服务器对微前端应用的用户进行身份验证和授权?明显我们的用户应该只需要认证一次,因此鉴权通常成为属于容器应用拥有的广泛关注的问题。容器可能有某种登录形式,我们通过它获得某种令牌。令牌由容器保存,可以在初始化时注入到每个微前端中。最终,微前端可以在任何发送给服务器的请求中携带令牌,然后服务器就可以执行任何需要的验证。 * * * -## Testing +## 测试 -We don't see much difference between monolithic frontends and micro frontends when it comes to testing. In general, whatever strategies you are using to test a monolithic frontend can be reproduced across each individual micro frontend. That is, each micro frontend should have its own comprehensive suite of automated tests that ensure the quality and correctness of the code. +在测试方面,我们认为笨重前端和微前端之间没有太大区别。通常来讲,你用来测试笨重前端的任何策略都可以应用于每个微前端。也就是说,每个微前端都应该有自己全面的自动化测试套件来保证代码的质量和正确性。 -The obvious gap would then be integration testing of the various micro frontends with the container application. This can be done using your preferred choice of functional/end-to-end testing tool (such as Selenium or Cypress), but don't take things too far; functional tests should only cover aspects that cannot be tested at a lower level of the [Test Pyramid](https://martinfowler.com/bliki/TestPyramid.html). By that we mean, use unit tests to cover your low-level business logic and rendering logic, and then use functional tests just to validate that the page is assembled correctly. For example, you might load up the fully-integrated application at a particular URL, and assert that the hard-coded title of the relevant micro frontend is present on the page. +明显的障碍是各种微前端与容器应用的集成测试。这个可以使用你喜欢的功能/端对端测试工具(比如 Selenium 或 Cypress)来完成,但是不要过度使用。功能测试应该只涵盖无法在[测试金字塔](https://martinfowler.com/bliki/TestPyramid.html)较低级别测试的方面。我们的意思是,使用单元测试涵盖低级别业务逻辑和渲染逻辑,功能测试只用来验证页面是否正确渲染。例如,你可以在特定 URL 上加载完全集成的应用程序,并断言相应微前端的硬编码标题出现在页面上。 -If there are user journeys that span across micro frontends, then you could use functional testing to cover those, but keep the functional tests focussed on validating the integration of the frontends, and not the internal business logic of each micro frontend, which should have already been covered by unit tests. [As mentioned above,](https://martinfowler.com/articles/micro-frontends.html#Cross-applicationCommunication) consumer-driven contracts can help to directly specify the interactions that occur between micro frontends without the flakiness of integration environments and functional testing. +如果用户的使用跨越微前端,那么你可以用功能测试来测试这些,但要保证功能测试专注于验证前端的整合,而不是每个微前端的内部业务逻辑,这应该已经被单元测试所涵盖。[正如刚才提到的](https://martinfowler.com/articles/micro-frontends.html#Cross-applicationCommunication),用户驱动的约定有助于直接指定微前端之间发生的交互,而不会出现集成环境和功能测试的瑕疵。 * * * -## The example in detail +## 案例详解 -Most of the rest of this article will be a detailed explanation of just one way that our example application can be implemented. We'll focus mostly on how the container application and the micro frontends [integrate together using JavaScript](https://martinfowler.com/articles/micro-frontends.html#Run-timeIntegrationViaJavascript), as that's probably the most interesting and complex part. You can see the end result deployed live at [https://demo.microfrontends.com](https://demo.microfrontends.com), and the full source code can be seen on [Github](https://github.com/micro-frontends-demo). +本文后面的大部分内容将详细解释我们的示例应用程序实现的一种方式。我们将重点关注容器应用和微前端如何[使用 JavaScript 整合在一起](https://martinfowler.com/articles/micro-frontends.html#Run-timeIntegrationViaJavascript),这可能是最有趣也最复杂的部分。你可以在 [https://demo.microfrontends.com](https://demo.microfrontends.com) 看到实时部署的最终结果,所有源代码都可以在 [Github](https://github.com/micro-frontends-demo) 上看到。 -![A screenshot of the 'browse' landing page of the full micro frontends demo application](https://martinfowler.com/articles/micro-frontends/screenshot-browse.png) +![整个微前端示例应用的首页“概览”截图](https://martinfowler.com/articles/micro-frontends/screenshot-browse.png) -Figure 8: The 'browse' landing page of the full micro frontends demo application +图 8:整个微前端示例应用的首页“概览” -The demo is all built using React.js, so it's worth calling out that React does **not** have a monopoly on this architecture. Micro frontends can be implemented with with many different tools or frameworks. We chose React here because of its popularity and because of our own familiarity with it. +该示例完全使用 React 开发,有必要说明的是,React **没有**垄断这个架构。可以使用许多不同的工具或框架来实现微前端。这里我们使用 React 是因为它的受欢迎程度以及我们对它的熟悉程度。 -### The container +### 容器 -We'll start with [the container](https://github.com/micro-frontends-demo/container), as it's the entry point for our customers. Let's see what we can learn about it from its `package.json`: +我们从[容器](https://github.com/micro-frontends-demo/container)开始,因为它是我们用户的入口。让我们从它的 `package.json` 中看看可以发现什么: ``` { @@ -96,11 +96,11 @@ We'll start with [the container](https://github.com/micro-frontends-demo/contain } ``` -From the dependencies on `react` and `react-scripts`, we can conclude that it's a React.js application created with [`create-react-app`](https://facebook.github.io/create-react-app/). More interesting is what's **not** there: any mention of the micro frontends that we're going to compose together to form our final application. If we were to specify them here as library dependencies, we'd be heading down the path of build-time integration, which [as mentioned previously](https://martinfowler.com/articles/micro-frontends.html#Build-timeIntegration) tends to cause problematic coupling in our release cycles. +从 `react` 和 `react-scripts` 依赖可以看出它是通过 [`create-react-app`](https://facebook.github.io/create-react-app/) 创建的 React 应用。更有趣的是那**没有**的:任何提及我们将要组成以形成我们的最终应用程序的微前端。如果我们在这里将它们指定为库依赖项,那么我们将走向构建时集成的道路,那就会[像之前提到的](https://martinfowler.com/articles/micro-frontends.html#Build-timeIntegration)会导致在我们的发布周期中有问题的耦合。 -> In version 1 of `react-scripts` it was possible to have multiple applications coexist on a single page without conflicts, but version 2 uses some webpack features that cause errors when two or more apps try to render themselves on the one page. For this reason we use `react-app-rewired` to override some of the internal webpack config of `react-scripts`. This fixes those errors, and lets us keep relying on `react-scripts` to manage our build tooling for us. +> `react-scripts` 1.x 版本可以在单个页面中拥有多个应用而不产生冲突,但在 2.x 版本使用一些 webpack 特性,当两个以上应用在单个页面渲染时会导致错误。基于这个原因我们使用 `react-app-rewired` 覆盖一些 webpack 内部的 `react-scripts` 配置。它会修复这些错误,让我们继续依靠 `react-scripts` 来管理我们的构建工具。 -To see how we select and display a micro frontend, let's look at `App.js`. We use [React Router](https://reacttraining.com/react-router/) to match the current URL against a predefined list of routes, and render a corresponding component: +为了了解我们如何选择和展示微前端,我们来看一下 `App.js`。我们使用 [React Router](https://reacttraining.com/react-router/) 将当前 URL 与预定义的路由列表进行匹配,并且渲染相应组件: ``` @@ -110,7 +110,7 @@ To see how we select and display a micro frontend, let's look at `App.js`. We us ``` -The `Random` component is not that interesting - it just redirects the page to a randomly selected restaurant URL. The `Browse` and `Restaurant` components look like this: +`Random` 组件不那么有趣 —— 它只是重定向到随机选择的餐厅 URL 对应的页面。`Browse` 和 `Restaurant` 是这样: ``` const Browse = ({ history }) => ( @@ -121,9 +121,9 @@ const Restaurant = ({ history }) => ( ); ``` -In both cases, we render a `MicroFrontend` component. Aside from the history object (which will become important later), we specify the unique name of the application, and the host from which its bundle can be downloaded. This config-driven URL will be something like `http://localhost:3001` when running locally, or `https://browse.demo.microfrontends.com` in production. +这两种情况,我们渲染 `MicroFrontend` 组件。除了 history 对象(后面会变得重要),我们指定应用的唯一名称,以及 bundle 下载的主机地址。在本地运行时,这个配置驱动的 URL 类似于 `http://localhost:3001`,生产环境则类似 `https://browse.demo.microfrontends.com`。 -Having selected a micro frontend in `App.js`, now we'll render it in `MicroFrontend.js`, which is just another React component: +在 `App.js` 中选择了一个微前端,现在我们将在 `MicroFrontend.js` 渲染它,这只是另一个 React 组件: ``` class MicroFrontend extends React.Component { @@ -133,13 +133,13 @@ class MicroFrontend extends React.Component { } ``` -This is not the entire class, we'll be seeing more of its methods soon. +这不是完整的类,我们很快会看到它更多的方法。 -When rendering, all we do is put a container element on the page, with an ID that's unique to the micro frontend. This is where we'll tell our micro frontend to render itself. We use React's `componentDidMount` as the trigger for downloading and mounting the micro frontend: +渲染时,我们要做的就是在页面上放置带有微前端唯一 ID 的容器元素。这是我们告诉微前端渲染自己的地方。我们使用 React 的 `componentDidMount` 作为下载和渲染微前端的触发器: -> `componentDidMount` is a lifecycle method of React components, which is called by the framework just after an instance of our component has been 'mounted' into the DOM for the first time. +> `componentDidMount` 是 React 组件的生命周期函数,它只会在组件实例首次在 DOM 中“渲染”时被框架调用。 -class MicroFrontend… +MicroFrontend 类…… ``` componentDidMount() { @@ -163,11 +163,11 @@ class MicroFrontend… } ``` -> We have to fetch the script's URL from an asset manifest file, because `react-scripts` outputs compiled JavaScript files that have hashes in their filename to facilitate caching. +> 我们必须从静态清单文件中获取脚本的 URL,因为 `react-scripts` 输出的编译后 JavaScript 文件名中包含便于缓存的哈希值。 -First we check if the relevant script, which has a unique ID, has already been downloaded, in which case we can just render it immediately. If not, we fetch the `asset-manifest.json` file from the appropriate host, in order to look up the full URL of the main script asset. Once we've set the script's URL, all that's left is to attach it to the document, with an `onload` handler that renders the micro frontend: +首先我们检查有唯一 ID 的相关脚本是否已经下载,如果下载了,我们可以立即渲染它。如果没有,我们获取从适当的主机获取 `asset-manifest.json` 文件,以便查找主脚本资产的完整 URL。一旦我们设置了脚本的 URL,剩下的就是将它附加到文档中,使用 `onload` 处理程序渲染微前端: -class MicroFrontend… +MicroFrontend 类 ``` renderMicroFrontend = () => { @@ -178,11 +178,11 @@ class MicroFrontend… }; ``` -In the above code we're calling a global function called something like `window.renderBrowse`, which was put there by the script that we just downloaded. We pass it the ID of the `
` element where the micro frontend should render itself, and a `history` object, which we'll explain soon. **The signature of this global function is the key contract between the container application and the micro frontends**. This is where any communication or integration should happen, so keeping it fairly lightweight makes it easy to maintain, and to add new micro frontends in the future. Whenever we want to do something that would require a change to this code, we should think long and hard about what it means for the coupling of our codebases, and the maintenance of the contract. +在上面的代码中我们调用了 `window.renderBrowse` 全局函数,它被我们刚刚下载的脚本放在那里。我们给微前端应该渲染的 `
` 元素分配一个 ID 和 `history` 对象,我们很快会解释。**这个全局函数的签名是容器应用和微前端之间的关键约定**。这是任何通讯或集成应该发生的地方,因此保持它相当轻量级使其易于维护,并在未来添加新的微前端。每当我们想要做一些需要更改此代码的事情时,我们应该仔细地思考它对于我们的代码库的耦合以及约定的维护意味着什么。 -There's one final piece, which is handling clean-up. When our `MicroFrontend` component un-mounts (is removed from the DOM), we want to un-mount the relevant micro frontend too. There is a corresponding global function defined by each micro frontend for this purpose, which we call from the appropriate React lifecycle method: +最后一件是处理清理工作。当我们的 `MicroFrontend` 组件卸载时(从 DOM 中移除),我们也想卸载相应的微前端。为此,每个微前端都定义了一个相应的全局函数,我们在适当的 React 生命周期方法中调用它: -class MicroFrontend… +MicroFrontend 类…… ``` componentWillUnmount() { @@ -192,9 +192,9 @@ class MicroFrontend… } ``` -In terms of its own content, all that the container renders directly is the top-level header and navigation bar of the site, as those are constant across all pages. The CSS for those elements has been written carefully to ensure that it will only style elements within the header, so it shouldn't conflict with any styling code within the micro frontends. +就它本身的内容而言,容器直接渲染的所有内容是网站的顶层头部和导航栏,因为这些在所有页面中都是不变的。这些元素的 CSS 已经过仔细编写,以确保它只对标题中的元素进行样式化,所以它不应该与微前端内的任何样式代码冲突。 -And that's the end of the container application! It's fairly rudimentary, but this gives us a shell that can dynamically download our micro frontends at runtime, and glue them together into something cohesive on a single page. Those micro frontends can be independently deployed all the way to production, without ever making a change to any other micro frontend, or to the container itself. +这就是容器应用的结尾!它相当初级,但这给了我们一个 shell,可以在运行时动态下载我们的微前端,并将它们粘合在一起形成一个单一页面上的内容。这些微前端可以单独部署在生产上,无需改变任何其他微前端或容器本身。 > **建议按照顺序阅读本系列文章:** > From 3e3da1ed0522757efbdc06e8d68f4c036e1f8acd Mon Sep 17 00:00:00 2001 From: Jenniferyingni <1097532862@qq.com> Date: Wed, 10 Jul 2019 14:21:47 +0800 Subject: [PATCH 18/68] =?UTF-8?q?Web=20=E6=B5=81=E5=BC=8F=E6=96=87?= =?UTF-8?q?=E5=AD=97=E6=8E=92=E7=89=88=E7=9A=84=E7=8E=B0=E7=8A=B6=20(#6071?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 流式字体排版的现状 * accessibility 释义修改 * 统一翻译为流式文字排版 * 格式调整 * 校对修改 * Update the-state-of-fluid-web-typography.md --- TODO1/the-state-of-fluid-web-typography.md | 167 ++++++++++----------- 1 file changed, 82 insertions(+), 85 deletions(-) diff --git a/TODO1/the-state-of-fluid-web-typography.md b/TODO1/the-state-of-fluid-web-typography.md index 26d8d8996f8..0a30c247d83 100644 --- a/TODO1/the-state-of-fluid-web-typography.md +++ b/TODO1/the-state-of-fluid-web-typography.md @@ -2,47 +2,47 @@ > * 原文作者:[Matej Latin](https://twitter.com/matejlatin) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/the-state-of-fluid-web-typography.md](https://github.com/xitu/gold-miner/blob/master/TODO1/the-state-of-fluid-web-typography.md) -> * 译者: -> * 校对者: +> * 译者:[Jenniferyingni](https://github.com/Jenniferyingni) +> * 校对者:[smilemuffie](https://github.com/smilemuffie), [mymmon](https://github.com/mymmon) -# The State of Fluid Web Typography +# Web 流式文字排版的现状 ![The State of Fluid Web Typography](https://betterwebtype.com/assets/img/posts/state-of-fluid-web-typography/post@2x.jpg) -Fluid typography gives us so many opportunities to better design the reading experiences on the web but, at the same time, it introduces problems of font sizes scaling uncontrollably and potential accessibility issues. Is fluid web typography ready to be used? +Web 流式文字排版可以提升用户的网站阅读体验,但与此同时,它也带来了字体不受控制变大的问题和对浏览器辅助功能使用的影响。那么 Web 流式文字排版到底能否在实践中被应用呢? -**Please note that this article uses animated gifs to illustrate the different approaches to responsive/fluid web typography and can be quite heavy for mobile devices.** +**请注意本文使用了 GIF 动画来演示使用响应式/流式文字排版的实现效果,移动端设备的访问者可能阅读上会有一定影响。** -Browser support for the viewport units has been good for a while. [Looking into it now](https://caniuse.com/#feat=viewport-units), most browsers fully supported them since 2013-2014. Microsoft’s Edge was an exception (no surprise there) as it only supports them since the Edge 16 version which was released in October 2017. Still, it’s two years since then. With all other major browsers supporting the viewport units for 5-6 years, how come we don’t encounter fluid typography on websites more often? I have to admit, I am one of those who resize the browser window to see how a website adjusts to the viewport change. I do that a lot, but I don’t remember the last time I encountered a website using fluid web typography. I know that CSS Tricks uses it, but I can’t think of other examples. So it makes me wonder—how come the adoption of this nifty web typography feature isn’t more wide-spread? +浏览器对视口单位(vw, vh, vmin, vmax)的支持已经有一段时间了。[从这里看来](https://caniuse.com/#feat=viewport-units),大多数浏览器在 2013-2014 年就开始完全兼容这些单位。微软的 Edge 是一个例外(并不惊讶),在 2017 年 10 月发布的 Edge 16 才开始兼容,但是也过去了两年的时间。为什么其他主流浏览器已经兼容视口单位长达 5-6 年的时间,我们还是很少看到采用流式文字排版的网页呢?我必须承认,我是那种访问网页时会调整浏览器窗口大小来看页面如何适应窗口大小变化的人之一。我经常这样做,但是我已经不记得上一次发现使用流式文字排版的网站是什么时候了。除了 CSS Tricks 之外想不到其他的例子。因此我就开始思考,为什么这种网页文字排版技巧没有被广泛的使用呢? -It’s hard to answer that question as I don’t think there’s one single thing that would be the cause. I think it boils down to: +回答这个问题并没有那么容易,因为原因并不是单方面的。我认为可以归结为以下几个: -1. good responsive web design/development still being hard and complex -2. web typography (still) isn’t perceived as important by many designers/developers -3. potential accessibility problems -4. fluid web typography can be very tricky. +1. 好的响应式 Web 设计/开发仍然是困难和复杂的。 +2. Web 文字排版仍旧没有引起设计者/开发者的重视。 +3. 潜在的对浏览器无障碍访问功能的影响。 +4. 流式文字排版是比较难实现的。 -Let’s take a closer look at each of these. +让我们来仔细分析下每个原因。 -## Good responsive web design is still hard and complex +## 好的响应式 Web 设计仍然是困难和复杂的 -A lot of energy and effort goes into the first of the problems listed above. The tools that we have at our disposal for building responsive websites are the best as they have been. It’s so much easier to build a good responsive website these days but it still takes quite an effort. Picking the tools, testing them and then adopting them if it seems they offer what we need takes a large portion of the time in a web design process. We haven’t truly come to the point yet where we simply design a website and the tools, somewhat automatically, make sure that the website works well on most devices. For example, we still design (draw static images of) our websites in digital design tools that are so far apart from what the actual end product is made of—code. These tools barely support the basic responsive web design techniques, we can’t even speak about fluid typography in them. +最耗费精力的是上面列出的第一个问题。和以前相比,响应式页面设计工具的进步使得构建一个好的响应式网站变得容易多了,但是仍然需要花费很多时间。在网页设计过程中,很大一部分时间花在了挑选符合功能需求的工具、测试工具、应用工具上。我们还没有做到只需设计好一个屏幕宽度下的视觉稿,工具就能自动产生在其他大多数屏幕宽度设备下的视觉显示。比如,我们仍然使用数字设计工具来绘制静态的视觉稿,这与最终代码实现的效果相差甚远。这些工具连基本的响应式网页设计都几乎不支持,更别说实现流式文字排版了。 -## Web typography isn’t perceived as important +## Web 文字排版没有引起重视 -The second one is something that I want to challenge with Better Web Type. There’s quite a bit of type geeks out there already but I think it’s kinda sad that it’s mostly these people that really care about the quality of typography used on the web. Sadly, a lot of web designers and developers still think of web typography as choosing a font and setting text sizes for their websites. +第二个问题是我希望通过 **Better Web Type** 这个网站来改善的,在那里有很多热衷于研究样式的极客们,但遗憾的是,只有这些人真正的关注网页中文字排版的质量,仍然有许多的设计师和开发者认为文字排版就是简单的给选择字体类型和调整字体大小。 -## Fluid web typography comes with accessibility problems +## 流式字体布局影响了无障碍访问功能的使用 -Too few people think about accessibility when it comes to designing and building websites. I want to look at this specifically as I do this exploration. I noticed a few accessibility problems with fluid web typography before but nobody really speaks about this. It only gets mentioned here and there. +在设计和构建网站的时候,很少有人会考虑到浏览器的无障碍访问功能。我想要在本文中特别关注一下这个问题。在大家还没有意识到这一点前,我就注意到流式文字排版对无障碍访问功能的影响,而这个问题几乎没有被人提及。 -## Fluid web typography is tricky +## 流式文字排版是比较难实现的 -Using viewport units for setting font sizes is tricky. I receive a lot of emails from people telling me that they experimented with fluid typography but never actually used it on a live website. But fluid web typography doesn’t need to be tricky. We just need ways to make it easy-attainable and better. And there’s a lot of smart people out there that have ways of making it work. Let’s take a look at a few approaches and see what works best and which approach makes fluid web typography less tricky, controllable and **better**. As we do so, let’s also take a look if there are any accessibility concerns when it comes to using this approach as well. +使用视口单位来设置字体大小是棘手的。我收到过许多封这样的邮件,有人告诉我他有尝试过使用流式文字排版,但是最终并没有选择将其应用到实际的网站中。但是流式文字排版不应该是那么难实现的,我们需要让它变得更容易,已经有许多聪明的人找到了对的方法。让我们来看看几个不同的实现方法,然后讨论哪个方法是最佳实践,哪个方法让流式布局不那么困难,变得更加的可控和**更好**。与此同时,我们也将看看这些方法有没有带来相应的无障碍访问问题。 -## Responsive typography +## 响应式文字排版 -Ok, so let’s start with what’s most common today. Our starting point into this journey of exploring fluid web typography is actually responsive web typography. Adopted with the most common approach to web design today (responsive web design), it’s popular to start with a mobile layout and font sizes, set a base font size and define other sizes based on that. This can be as simple as the following: +那我们从最常见的开始说起。我们最开始探索流式文字排版其实是从响应式文字排版开始的。如果采用现在最常用的网页设计(响应式网页设计),通常是从移动端布局和字体大小开始,我们通常会设定一个根节点的字体大小,然后在这个字体大小的基础上去定义其它的大小。以下是一个最简单的示例: ```scss html { @@ -54,9 +54,9 @@ h1 { } ``` -**Please note, all the code snippets use SCSS.** +**请注意,所有的代码段均是使用了 SCSS。** -Here we set our base font size to match the browser’s default font size (usually 16px). This is an important step when it comes to creating websites with accessibility in mind, we’ll come back to this later. But then, we need to think about other devices and screen sizes and re-arrange the layout of our website and redefine the font sizes accordingly. So we use media queries to define the breakpoints at which these shifts in layout and font sizes happen. +在这里我们将根节点字体大小匹配浏览器的默认字体大小(通常是 16px)。如果要创建支持无障碍访问功能的网站,这是很重要的一步,我们稍后将会回过头来看这一步。现在,我们需要考虑到其他不同屏幕大小的设备,重新给我们的网页布局并且重新定义字体的大小。因此,我们使用媒体查询来定义布局和字体大小发生变化的断点。 ```scss html { @@ -76,35 +76,35 @@ h1 { } ``` -In the example above, we redefined our base font size, we increased it for what we believe will be seen on a typical tablet screen. With a larger screen, we have more room to work with so our changes in font sizes can be more obvious. So we also increase the size of our heading 1 to `3.5rem` instead of the default `3rem`. +在上面的例子中,我们重新定义了根节点的字体大小,我们认为平板电脑大小的尺寸是 768 px,并定义了在这个尺寸下合适的字体大小 。更大的屏幕有更多的空间,因此字体大小的改变也可以更明显。因此我们将一级标题的大小从默认的 `3rem` 修改为了 `3.5rem`。 -Take a look at [this basic responsive typography example](https://codepen.io/matejlatin/full/oRLaXy) on CodePen. This works well, it’s supported by all browsers and has no accessibility issues. So why do we need to improve it? A lot of people worry about the fact that font resizing only happens at the breakpoints we define. In our example above, the reader of our website would see paragraphs of text set at 16 pixels all the way up to a screen width of 768 pixels. If they were reading our website on a tablet, they’d get the base font size of 18 pixels (112.5%). I think the main problem with this mentality is that they’re focusing too much on their own point of view, instead of focusing on the user. They resize their browser window and see something like the following: +看看 CodePen 上这个[响应式布局的简单示例](https://codepen.io/matejlatin/full/oRLaXy)。所有的浏览器都支持且没有无障碍访问的问题,那么为什么我们还要改进它呢?字体大小只会在我们定义的断点发生改变,这是很多人会担心的问题。在上面的示例中,我们网站的用户会发现字体的大小一直为 16px,直到屏幕的宽度变成了 768px 才发生变化。如果用户在平板电脑上访问我们的网站,他看到的根节点的字体大小是 18px(112.5%)。在我看来,这个方法最主要的问题是他们过分关注了自己的观点而忽略了用户。如果调整窗口的大小将会看到这样的现象: ![Responsive typography: fonts only resize at predefined breakpoints which results in drastic shifts like these.](https://betterwebtype.com/assets/img/posts/state-of-fluid-web-typography/responsive.gif) -Responsive typography: fonts only resize at predefined breakpoints which results in drastic shifts like these. +响应式布局:字体大小只会在预定义的断点处发生变化,这会导致字体像这样发生突变。 -Yes, the font sizes only change at the breakpoints we defined but we’re forgetting that the users have complete control over the text size through the settings on their device. If they’re accessing the website from a desktop computer and 18 pixels base font size isn’t large enough for them, they can easily resize it (if we don’t take this control away from them by overriding the default font size with non-relative units, like pixels, in CSS). +是的,字体大小只在我们定义的断点处发生变化,但是我们忘记了用户对于他们设备上的字体大小有绝对的控制权。如果他们从电脑端访问并且觉得 18px 的字体大小不够大,他们可以轻易地调整(如果我们没有使用例如 px 一样的非相对单位来覆盖默认的字体大小)。 -### Pros +### 优点 -* Users remain in control of font size, no accessibility issues. -* With a good approach, a few media queries can cover a large part of different screen widths. +* 用户保持对字体大小的控制权,没有无障碍访问问题。 +* 运用得当的情况下,少量的媒体查询可以覆盖大部分不同屏幕宽度的设备。 -### Cons +### 缺点 -* Font sizes are mostly static and changes are tied to pre-defined breakpoints (screen widths). -* Lots of whitespace on large screens, font sizes aren’t adaptive, we end up with websites that use a centred, one-column layout. +* 字体大小在大多数情况下是维持不变的且受限于事先定义的断点(屏幕宽度)。 +* 大屏幕上有大量的空白,字体大小不自适应,最终的网站将呈现为居中的单列布局。 -## [](#fully-fluid-typography)Fully fluid typography +## 纯流式文字排版 -Ok, so we can conclude that the responsive typography approach isn’t ideal. It kinda works but we want something better. Something we can have better control over. **We want our font size to be ideal on every screen size, not just on a specific mobile screen, a tablet screen, a desktop…** Fluid typography to the rescue! Well, we’ll see about that. +我们得出的结论是响应式文字排版不是理想的解决方案。它勉强满足需求但是我们想要有能够更好的控制字体大小的方案。**我们希望我们的字体大小在任何屏幕尺寸都是理想的,而不仅是在某个特定的移动端、平板或是电脑屏幕等**。流式文字排版就是我们的救星! -Viewport units have been introduced a while ago. The idea is simple, we can use the viewport height and viewport width units to define the size of anything in CSS. Including font sizes. In my opinion, the viewport units were introduced more as a way to scale specific and mostly fixed types of website layouts, similarly to how Sebastian Eberlein describes it [in his Hacker Noon article](https://hackernoon.com/using-viewport-units-to-scale-fixed-layouts-869638bb91f9). +视口单位的引入已经有一段时间了。这个想法很简单,我们可以在 CSS 中使用视口高度单位和视口宽度单位来定义任何元素的大小,包括字体大小。在我看来,视口单位被引入更多是作为绝对单位网页布局的一种扩展,这和 Sebastian Eberlein 在他的 [Hacker Noon 上的文章](https://hackernoon.com/using-viewport-units-to-scale-fixed-layouts-869638bb91f9)中所做的描述的相似。 -But nevertheles, viewport units have been used as a way to fluidly scale font sizes for a long time. Chris Coyier wrote about it back in 2012 and got excited about it because we know that ideal width of a line of text is around 80 characters and because “**these units allow you to get it feeling perfect and then have that experience scale to any size screen” [1]**. +但是尽管如此,视口单位很长一段时间来都被用作流畅地缩放字体大小的方法。Chris Coyier 在 2012 年在文章中兴奋地提到,一行文字理想的宽度大约是 80 个字符,“**这些单位能让你有完美的体验,并且能将这种体验扩展到所有大小的屏幕**”[1]。 -That’s all cool but it isn’t so simple in practice. Let’s say we do something like the following: +这听起来很酷,但实际上并不那么简单。假设我们执行以下操作: ```scss html { @@ -116,27 +116,27 @@ h1 { } ``` -We define the base font size with a viewport width unit. This means the font size will get resized for **every change in screen width**, not just the predefined breakpoints as was the case with responsive typography. But because the shifts in screen widths are so drastic (mobile sizes compared to laptops and desktop computers) the font size changes very rapidly. Take a look at this [fluid typography example on CodePen](https://codepen.io/matejlatin/full/awxyLG/) and try to resize the browser window. You’ll see something like the following: +我们使用视口单位来定义根节点字体大小。这意味着在**每此屏幕宽度变化**时字体大小都会改变,而不是像响应式布局一样只在几个预定义的断点。但是因为屏幕的宽度的差别是很大的(移动端屏幕大小相比于笔记本或者是台式电脑屏幕大小),所以字体的大小就会变化得非常快。来看一下 CodePen 上的 [流式布局示例](https://codepen.io/matejlatin/full/awxyLG/),当你调整窗口的大小时,会看到这样的现象: ![Fully fluid typography: font sizes change rapidly depending on the screen width.](https://betterwebtype.com/assets/img/posts/state-of-fluid-web-typography/fluid.gif) -Fully fluid typography: font sizes change rapidly depending on the screen width. +纯流式文字排版:字体大小根据屏幕宽度快速的变化。 -Can you see how tiny the font sizes get on smaller (supposedly mobile) screen sizes? That‘s way too small, how can we fix it? +是否注意到了字体在小屏幕(移动端)中的变化?最终的显示效果太小了,那么我们如何能修复这个问题呢? -### Pros +### 优点 -* In practice, I don’t see any pros of using this approach without combining it with responsive typography. +* 在实践中,如果流式文字排版不和响应式布局一起使用的话,是毫无优势可言的。 -### Cons +### 缺点 -* One downside to using Viewport units for defining font sizes is that they aren’t accessible (users don’t have control over font sizes in certain browsers). -* Using viewport units for font sizes also overrides user’s default font size (set in browser), another accessibility issue. -* Uncontrollable, font sizes scale rapidly from one extreme to the other. +* 影响无障碍访问功能的使用(用户在某些浏览器中不能控制字体的大小)。 +* 会覆盖用户在浏览器中设置的默认的字体大小,这是另外一个对无障碍访问功能使用的影响。 +* 字体会不受控制的快速变大或变小。 -### Adding lots of media queries to make it work +### 增加媒体查询来改善实现效果 -So the fully fluid web typography approach clearly doesn’t work. Ironically enough, the only way to kinda make it work is to use the responsive typography techniques to try to control the rapidly-changing font sizes. But, unlike in our responsive web typography approach, in this case we need a lot of media queries to limit the resizing of fonts. So we can do something like this: +显然纯流式文字排版是满足不了需求的。讽刺的是,改善它的唯一办法就是采用响应式文字排版来控制字体大小的快速变化。但是,与我们前面的响应式文字排版例子不同的是,在这个案例中我们使用了多个媒体查询来限制字体大小的变化。我们可以这样做: ```scss $breakpoint-1: 25em; @@ -166,35 +166,35 @@ h1 { } ``` -Take a look at this [fluid and responsive web typography example](https://codepen.io/matejlatin/full/GELvGK/) on CodePen and try resizing the browser window. You’ll see that it’s better than the fully fluid approach above, we could say that it kinda works (I used seven breakpoints to make it work). +在 CodePen 中看一下这个[流式文字排版 + 响应式文字排版的例子](https://codepen.io/matejlatin/full/GELvGK/) 并且调整浏览器窗口的大小,你会发现效果比上面的纯流式布局要好很多,这基本满足了我们的需求(示例中我使用了 7 个断点)。 ![Fluid + responsive typography: we limit the scaling of font sizes for small and large screens.](https://betterwebtype.com/assets/img/posts/state-of-fluid-web-typography/fluid+responsive.gif) -Fluid + responsive typography: we limit the scaling of font sizes for small and large screens. +流式布局 + 响应式布局:限制了小屏幕和大屏幕中字体大小的变化。 -I don’t know about you, but to me, this looks like too much work and trouble for the few benefits we get in return. Yes, our font sizes scale in a nicer manner than just the responsive web typography approach, but it also brings other problems. Including the accessibility problems which for me is a no-no. +我不知道你怎么看,但是对我来说,这种做法的投入和产出比是不太理想的,我不否认它比纯响应式文字排版实现的效果更好,但是它也引入了其它的问题,包括无障碍访问的问题,这对我来说是无法接受的。 -#### Pros +#### 优点 -* Fonts sizes scale nicely and adapt to each screen width. +* 字体大小的变化比较理想,且能适应所有的屏幕大小。 -#### Cons +#### 缺点 -* One downside to using Viewport units for defining font sizes is that they aren’t accessible (users don’t have control over font sizes in certain browsers). -* Because we use the media queries to control the scaling of font sizes, we still get these jumps in sizes on resizing the browser window. -* Using viewport units for font sizes also overrides user’s default font size (set in browser), another accessibility issue. +* 影响无障碍访问功能的使用(用户在某些浏览器中不能控制字体的大小)。 +* 因为使用了媒体查询来控制字体大小,在缩放屏幕大小的时候仍然会在断点处发生突变。 +* 会覆盖用户在浏览器中设置的默认的字体大小,这是另外一个对无障碍访问功能使用的影响。 -## [](#fluid-typography-with-css-locks)Fluid typography with CSS locks +## 带CSS锁的流式文字排版 -Others have noticed the problem of lack of control over font sizes in the fluid web typography approach. They knew that adding lots of media queries isn’t desirable or ideal so they tried to find an alternative. Mike Riethmuller [2], Tim Brown [3] and Geoff Graham [4] all eventually came to the conclusion that the **Fluid typography with CSS locks** is probably the best approach. +另外一批人注意到了流式文字排版字体大小不受控制变化的问题。他们知道增加许多的媒体查询并不是理想的解决方案,所以他们开始寻找另外的解决方案。Mike Riethmuller [2], Tim Brown [3] 和 Geoff Graham [4] 最终得出的结论都是**带 CSS 锁的流式文字排版**可能是最好的方案。 -This is an interesting technique. It sets the minimum font size and at what screen width it should be used, the maximum font size and at what screen width it should be used and all the fluid font sizes between the two. It elegantly addresses the problem of font sizes scaling too rapidly and it uses basic math to do so. The approach is best described with the image below. Font sizes don’t change in the left and right compartments, they only scale in the middle one. Lower gate is the minimum screen size and we apply a fixed font size to it, the same for the upper gate which is the maximum screen size. +这是一个很有意思的方案。它设定了在小屏幕下适用的最小字体大小,以及在大屏幕下适用的最大字体大小。在这两个屏幕大小中间的范围,字体大小的变化就采用流式文字排版。这个方法使用基础的数学原理优雅的解决了字体大小变化过快的问题。下图能很好的解释这种方法,字体大小只在中间的区域发生变化,在左侧和右侧均不会变化。左边的阀门是指最小的屏幕大小,我们对其使用固定的字体大小,同理右边的阀门是指最大的屏幕大小。 ![CSS locks for fluid typography, font sizes only scale in the middle compartment.](https://betterwebtype.com/assets/img/posts/state-of-fluid-web-typography/lock-basic.png) -CSS locks for fluid typography, font sizes only scale in the middle compartment. ([Source](https://blog.typekit.com/2016/08/17/flexible-typography-with-css-locks/)) +带 CSS 锁的流式文字排版,字体仅在中间部分变化大小。([链接](https://blog.typekit.com/2016/08/17/flexible-typography-with-css-locks/)) -The formula to make this work is relatively simple, it takes 4 variables: **minimum size**, **maximum size**, **minimum viewport width** and **maximum viewport width**. +实现这个功能的公式非常的简单,需要用到四个变量:**最小字体值**、**最大字体值**、**最小视口宽度**、**最大视口宽度**。 ```scss body { @@ -202,45 +202,42 @@ body { } ``` -And if you take a look at the [live example of fluid typography with CSS locks on CodePen](https://codepen.io/matejlatin/full/dEXQmG) and try to resize the browser window you’ll get the following. +访问 CodePen 上[带 CSS 锁的流式文字排版示例](https://codepen.io/matejlatin/full/dEXQmG),调整浏览器窗口的大小将会看到下面的现象。 ![CSS locks in action. Font sizes change only in the specified range.](https://betterwebtype.com/assets/img/posts/state-of-fluid-web-typography/css-locks.mov.gif) -CSS locks in action. Font sizes change only in the specified range. +CSS 锁定了变化。字体大小只在特定的区间范围内改变。 -This works satisfyingly nice. Font sizes scale nicely if the screen width is between the minimum and maximum. If the screen width is either smaller than the minimum or larger than the maximum, we switch to the fixed font size. In theory this works very well. But when I tried changing my browser’s default font size on Chrome, it had no effect on the CodePen example above. That’s a problem because it means that this approach still ignores user’s default font size and therefore causes accessibility problems. Certain browsers also don’t resize the font size on a website when the user tries to resize it (⌘+ on a Mac). +这个实现效果是令人满意的。如果屏幕宽度介于最小值和最大值之间,字体大小可以很好地缩放。如果屏幕宽度小于最小值或者是大于最大值,将切换到固定的字体大小。理论上是行得通的,但是当我在 Chrome 上改变了浏览器的默认字体大小,会发现没有产生 CodePen 示例中的效果。这表明这个方法仍然忽略了可变的用户默认字体大小,存在对无障碍访问功能的影响。某些浏览器中用户无法改变字体的大小(在 Mac 上使用 ⌘+)。 -### Pros +### 优点 -* No jumps in scaling font sizes on resizing the browser window. -* Font sizes don’t scale below the minimum and above the maximum screen width. +* 调整浏览器窗口大小的时候字体大小不会在断点突变。 +* 当屏幕宽度小于最小值或者大于最大值时,字体大小不会改变。 -### Cons +### 缺点 -* One downside to using Viewport units for defining font sizes is that they aren’t accessible (users don’t have control over font sizes in certain browsers). -* Using viewport units for font sizes also overrides user’s default font size (set in browser), another accessibility issue. +* 影响无障碍访问功能的使用(用户在某些浏览器中不能控制字体的大小)。 +* 会覆盖用户在浏览器中设置的默认的字体大小,这是另外一个对无障碍访问功能使用的影响。 -## [](#conclusionshould-we-use-fluid-web-typography)Conclusion—should we use fluid web typography? +## 结论 — 我们是否应该使用流式文字排版? -At first look, the CSS locks approach looks like the best one. It definitely solves the problem of font sizes scaling uncontrollably. It only uses a small number of media queries and it doesn’t get major shifts in scaling font sizes. My problem with all of the fluid web typography techniques is that they all override user’s default font size set in the browser (from my tests, this was only the case with Chrome). And in some cases and in some browsers, the font size can’t be resized by the user because the viewport units are used. This means that none of these solutions is completely compliant from the accessibility point of view. +乍一看,CSS 锁似乎是最佳的选择,它解决了字体大小过大或者是过小的问题,只需要使用少量的媒体查询并且字体变化过程中避免了断点突变。而使用流式文字排版的方案普遍存在的一个问题是它们都覆盖了浏览器中用户设置的默认字体大小(在我的测试中,仅在 Chrome 中发现了这个问题)。在某些浏览器的某些情况下,因为使用了视口单位用户不能改变字体的大小。这意味着从无障碍访问功能的角度来看,这些解决方案都不完全符合要求。 -> Fluid typography doesn’t play nicely with the default browser font size and therefore shouldn’t be used until that changes. +> 流式文字排版与默认浏览器字体大小两者不能很好的地兼容,因此在这一问题解决之前不推荐使用。 -When writing about the fully fluid typography approach, I wrote that “**We want our font size to be ideal on every screen size, not just on a small mobile screen, a tablet screen, a desktop…”.** I think that this approach is completely wrong. We shouldn’t be focusing on making our font sizes scale nicely for every screen width. We should make our texts easy and enjoyable to read to each and every one of our users. This means leaving some of the control over web typography in their hands. At this moment, fluid web typography interferes too much with that. If it played nicely with the default browser font size I’d say go for it, but because it doesn’t, I must say it’s not an approach we should be using. Not at this time at least. Just like I said when I explored fluid typography in my book, I’m **still** sticking with responsive web typography. +在写纯流式文字排版时,我曾写过“**我们希望我们的字体大小在任何屏幕尺寸都是理想的,而不仅是在某个特定的移动端、平板或是电脑屏幕等**”。现在我觉得这个想法是完全错误的,我们不应该把关注点放在让字体大小在所有窗口尺寸下合理地缩放,而应当放在如何让每一个用户都能轻松且愉悦地阅读网站的内容上。从这点来看,流式文字排版对用户的干涉太多了。如果它能和浏览器默认字体大小很好地兼容,那我会鼓励大家来使用,但是它没有满足这一点,所以我们认为它不是一个能被采用的方案,至少现在还不是。正如我在书中探索流式文字排版时所提及的,我**依旧**坚持响应式文字排版。 [![Better Web Type book](https://betterwebtype.com/assets/img/book-image-centred.png)](/web-typography-book/) -Did you enjoy what you just read? Great, I have much more of this stuff in my [book about web typography](/web-typography-book/) that I wrote. It’s very popular with web designers and developers, you should definitely check it out. +如果你喜欢本文的内容,可以在[我的书](https://betterwebtype.com/web-typography-book/)中阅读到更多的内容。这本书在设计师与前端开发者中很受欢迎,请一定不要错过。 --- -1. **Viewport Sized Typography** by Chris Coyier ([Source](https://css-tricks.com/viewport-sized-typography/)) - -2. **Precise Control Over Responsive Typography** by Mike Riethmuller ([Source](https://www.madebymike.com.au/writing/precise-control-responsive-typography/)) - -3. **Flexible Typography with CSS Locks** by Tim Brown ([Source](https://blog.typekit.com/2016/08/17/flexible-typography-with-css-locks/)) - -4. **Fluid Typography** by Geoff Graham ([Source](https://css-tricks.com/snippets/css/fluid-typography/)) +1. **Viewport Sized Typography** by Chris Coyier ([链接](https://css-tricks.com/viewport-sized-typography/)) +2. **Precise Control Over Responsive Typography** by Mike Riethmuller ([链接](https://www.madebymike.com.au/writing/precise-control-responsive-typography/)) +3. **Flexible Typography with CSS Locks** by Tim Brown ([链接](https://blog.typekit.com/2016/08/17/flexible-typography-with-css-locks/)) +4. **Fluid Typography** by Geoff Graham ([链接](https://css-tricks.com/snippets/css/fluid-typography/)) > 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 From 0bece03e454565efd75a9663da5c11c360818171 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=95=E6=99=9F?= Date: Thu, 11 Jul 2019 13:43:08 +0800 Subject: [PATCH 19/68] =?UTF-8?q?=E7=BA=A0=E9=94=99=20(#6112)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO/a-crash-course-in-assembly.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO/a-crash-course-in-assembly.md b/TODO/a-crash-course-in-assembly.md index d21f142f4e5..a57197640f1 100644 --- a/TODO/a-crash-course-in-assembly.md +++ b/TODO/a-crash-course-in-assembly.md @@ -38,7 +38,7 @@ ![6-bits being taken from a 16-bit instruction and being piped into the ALU](https://2r4s9p1yi1fa2jd7j43zph8r-wpengine.netdna-ssl.com/files/2017/02/03-03-computer_architecture12-500x354.png) -接下来大脑会取后续两个三字节的字段来确定要相加的两个数。这两个数会存储在寄存器中。 +接下来大脑会取后续两个三位的字段来确定要相加的两个数。这两个数会存储在寄存器中。 ![Two 3-bit chunks being decoded to determine source registers](https://2r4s9p1yi1fa2jd7j43zph8r-wpengine.netdna-ssl.com/files/2017/02/03-04-computer_architecture17-500x352.png) From c1f9290254fa73487375b5f6f4255c493e89c051 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=95=E6=99=9F?= Date: Thu, 11 Jul 2019 13:43:34 +0800 Subject: [PATCH 20/68] =?UTF-8?q?=E7=BA=A0=E9=94=99=20(#6111)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO/creating-and-working-with-webassembly-modules.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO/creating-and-working-with-webassembly-modules.md b/TODO/creating-and-working-with-webassembly-modules.md index 85120b78bd3..7ec42ce3849 100644 --- a/TODO/creating-and-working-with-webassembly-modules.md +++ b/TODO/creating-and-working-with-webassembly-modules.md @@ -149,7 +149,7 @@ Emscripten 包含了许多附加工具和库来支持移植整个 C/C++ 代码 3. **启动(Start)**。当 WebAssembly 组件载入时自动运行的函数(基本上类似一个主函数)。 4. **全局变量(Global)**。为组件声明全局变量。 5. **内存(Memory)**。定义组件将使用到的内存空间。 -6. **表(Table)**。使把值映射到 WebAssembly 组件外部成为可能,就像 JavaScript 对象那样。这对于允许简介函数调用相当有用。 +6. **表(Table)**。使把值映射到 WebAssembly 组件外部成为可能,比如 JavaScript 对象。这对于允许间接函数调用相当有用。 7. **数据(Data)**。初始化导入或本地内存。 8. **元素(Element)**。初始化导入或本地的表。 From b72444e90907723d60bc66628288cc9ad0c989c6 Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 11 Jul 2019 14:14:36 +0800 Subject: [PATCH 21/68] =?UTF-8?q?=E6=8E=A8=E5=B9=BF=20PWA=20=E5=AE=89?= =?UTF-8?q?=E8=A3=85=E7=9A=84=E6=A8=A1=E5=BC=8F=EF=BC=88=E7=A7=BB=E5=8A=A8?= =?UTF-8?q?=E7=AB=AF=EF=BC=89=20(#6087)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update promoting-install-mobile.md 翻译完成 * 推广 PWA 安装的模式(移动端) 翻译完成 * 推广 PWA 安装的模式(移动端) 翻译完成 * Update promoting-install-mobile.md * 校对修改完成 校对修改完成 * Update promoting-install-mobile.md 添加校对者 --- TODO1/promoting-install-mobile.md | 230 +++++++++++++++--------------- 1 file changed, 114 insertions(+), 116 deletions(-) diff --git a/TODO1/promoting-install-mobile.md b/TODO1/promoting-install-mobile.md index f8572fb27ec..47f15740383 100644 --- a/TODO1/promoting-install-mobile.md +++ b/TODO1/promoting-install-mobile.md @@ -2,216 +2,214 @@ > * 原文作者:[Peter Mclachlan](https://developers.google.com/web/resources/contributors/pjmclachlan?hl=zh-cn) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/promoting-install-mobile.md](https://github.com/xitu/gold-miner/blob/master/TODO1/promoting-install-mobile.md) -> * 译者: -> * 校对者: +> * 译者:[Sam](https://github.com/xutaogit) +> * 校对者:[wuyanan](https://github.com/wuyanan),[柯小基](https://github.com/lgh757079506) -# Patterns for Promoting PWA Installation (mobile) +# 推广安装 PWA 的模式(移动端) -Progressive Web Apps (PWAs) are a [pattern](https://developers.google.com/web/progressive-web-apps/?hl=zh-cn) for creating app-like, instant loading, reliable and installable websites. Although PWAs are available for all devices, including [Desktop](https://developers.google.com/web/progressive-web-apps/desktop?hl=zh-cn), this article focuses on mobile PWA install promotion patterns. +渐进式网页应用(PWA)是一种[模式](https://developers.google.com/web/progressive-web-apps/?hl=zh-cn),用于创建类应用程序,即时加载的,可靠的和可安装的网站。虽然 PWA 技术适用于包括[桌面端](https://developers.google.com/web/progressive-web-apps/desktop?hl=zh-cn)在内的所有设备,本篇文章重点介绍移动端 PWA 安装推广模式。 -Why would you want a user to install your app to their home screen? The same reason you’d want a user to install your app from any app store. Users who install are your most engaged audience. Users who install a PWA have better engagement metrics than typical visitors, including more repeat visits, longer time on site and higher conversion rates, often at parity with native app users on mobile devices. +为什么你希望用户在他们的主页安装你的应用?和你希望用户从任何应用商店安装你的应用原因是一样的。安装了应用的用户是你最需关注的群体。安装了 PWA 的用户比一般的访客拥有更好的参与度指标,包括更频繁的访问次数,在页面上更久的停留时间以及更高的用户转化率,这些特点基本可以和移动设备上的本机应用相匹敌了。 -**If your PWA has use cases where it’s helpful for a user to install your app, for example if you have users who use your app more than once a week, you should be promoting the installation of your PWA within the web UI of your app.** +**如果你的 PWA 有助于让用户安装你 app 的场景,例如,用户每周使用你的应用超过一次,那么你应该在 app 里使用网页 UI 技术推广你 PWA 的安装。** -**Note:** See [Add to Home Screen (Web)](https://developers.google.com/web/fundamentals/app-install-banners/?hl=zh-cn) for the code required to implement a PWA install promotion. +**注意:**有关实施 PWA 安装推广所需的代码,请参阅[添加到主屏幕(Web)](https://developers.google.com/web/fundamentals/app-install-banners/?hl=zh-cn)。 -## PWA install promotion best practices +## PWA 安装推广的最佳实践 -Some best practices apply no matter what promotional patterns you’re using on your site. +无论你在网站上使用哪种推广模式,这些最佳做法都适用。 -1. Keep promotions outside of the flow of your user journeys. For example, in a PWA login page, put the call to action below the login form and submit button. Disruptive use of promotional patterns reduce the usability of your PWA and will negatively impact your engagement metrics. -2. Include the ability to dismiss or decline the promotion. Remember the user’s preferences if they do this and only re-prompt if there’s a change in the user’s relationship with your content such as if they signed in or completed a purchase. -3. Combine more than one of these techniques in different parts of your PWA, but be careful not to overwhelm or annoy your user with install promotion. Remember rule #1! -4. Only show the promotion when you [detect](https://developers.google.com/web/fundamentals/app-install-banners/?hl=zh-cn#listen_for_beforeinstallprompt) that the `beforeinstallprompt` event has been fired. +1. 将推广与你的用户操作流程相分离。举例来说,在 PWA 的登录页,将请求操作放在登陆表单和提交按钮下方。混乱的使用推广模式会降低你 PWA 的可用性,并且会对参与度指标造成负面影响。 +2. 需包含关闭或减少推广的能力。如果他们这样做,请记住用户的偏好,并且只有在用户与你的应用内容的关系发生变化时才会重新提示,例如他们已登录或完成购买。 +3. 可以在你 PWA 应用的不同部分里结合使用多种技术,但请注意不要过多的推广安装或打扰到你的用户。记住第一条规则 #1! +4. 仅在你[检测](https://developers.google.com/web/fundamentals/app-install-banners/?hl=zh-cn#listen_for_beforeinstallprompt)到 `beforeinstallprompt` 事件被触发时才显示推广信息。 -## Automatic browser promotion +## 浏览器自动推广 ![](https://developers.google.com/web/updates/images/2018/06/a2hs-infobar-cropped.png?hl=zh-cn) -Example of the Add to Home screen mini-infobar for AirHorner +在主屏页添加 AirHorner 迷你信息栏的示例。 -The browser already tells the user about the installability of your PWA using a mini-infobar when your PWA passes the [installability checklist](https://developers.google.com/web/fundamentals/app-install-banners/?hl=zh-cn) on Android. The mini-infobar is only meant as a helper, and it will go away in the future. +当你的 PWA 通过了 Android 上的[可安装性检查清单](https://developers.google.com/web/fundamentals/app-install-banners/?hl=zh-cn)时,浏览器就会告诉用户使用迷你信息栏 PWA 的可安装性。这个迷你信息栏只是一个辅助,未来它将被移除。 -**Note:** Starting in Chrome 76, you can [prevent the mini-infobar from appearing](https://developers.google.com/web/updates/2019/05/mini-infobar-update?hl=zh-cn) by calling `preventDefault` on the `beforeinstallprompt` event. If you do not call `preventDefault`, the banner will be shown the first time a user visits your site and it meets the [installability criteria](https://developers.google.com/web/fundamentals/app-install-banners/?hl=zh-cn) on Android, and then again after approximately 90 days. +**注意:**从 Chrome 76 版本开始,你可以通过在 `beforeinstallprompt` 事件上调用 `preventDefault` [阻止迷你信息栏出现](https://developers.google.com/web/updates/2019/05/mini-infobar-update?hl=zh-cn)。如果你不调用 `preventDefault`,该条目会在用户第一次访问你的站点时被显示出来,并且这符合安卓的[可安装标准](https://developers.google.com/web/fundamentals/app-install-banners/?hl=zh-cn),然后在大约 90 天以后它会再次显示。 -## Application UI promotional patterns +## 应用 UI 推广模式 -Application UI promotional patterns can be used for almost any kind of PWA and appear in the application UI, such as site navigation, banners, etc. As with any other type of promotional pattern, it’s important to be aware of the user’s context to minimize disruption of the user’s journey. +应用 UI 推广模式几乎适用于任何类型的 PWA 应用并在诸如网站导航,banner 等 UI 层面上体现出来。和使用任何其他类型的推广模式一样,很重要的一点是关照到用户的使用环境,尽量减少对用户操作流程的干扰。 -Sites which are thoughtful about when they trigger promotion UI achieve a larger number of installs and avoid interfering with the journeys of users who aren’t interested in installation. +在触发推广 UI 时,考虑周全的网站可以实现更多数量的安装,并避免干扰那些对安装不感兴趣的用户的操作流程。 -### Fixed header +### 固定标题 -This is an install button that is part of the header of your site. Other header content often includes site branding such as a logo and the hamburger menu. Headers may be `position:fixed` or not depending on your site’s functionality and user needs. +这里有一个安装按钮,它是你网站标题的一部分。其他标题内容通常包含网站品牌信息,诸如 logo 图标或者汉堡菜单。标题是否 `position:fixed` 取决于你的站点功能和用户需求。 ![](https://developers.google.com/web/fundamentals/app-install-banners/images/install-promo/header.png?hl=zh-cn) -When used appropriately, promoting PWA install from the header of your site is a great way to make it easier for your most loyal customers to return to your experience. Pixels in your PWA header are extremely valuable, so make sure your install call to action is appropriately sized, of greater importance than other possible header content, and unintrusive. +如果使用得当,在你的站点标题处推行 PWA 安装是一种很好的方式,因为这可以让你的大多数忠实用户更轻松地回归到你的体验上来。PWA 标题里的每个像素都很有价值,所以请确保你的安装请求操作具有合适的大小,并且比其他任何可能的标题内容更重要,还不那么显眼。 -Make sure you: +你必须确保: -* Evaluate the value of your installed use case for your users. Consider selective targeting to only present this promotion for users that are likely to benefit from it. -* Use precious header space efficiently. Consider what else would be helpful to offer your user in the header, and weigh the priority of the install promotion relative to other options. +* 评估用户已安装用例的价值。考虑选择性定位的向那些可能从中受益的用户展示这些推广信息。 +* 有效使用宝贵的标题空间。考虑在标题中可以为你的用户提供哪些其他帮助,并权衡安装推广相对于其他选项的优先级(译者注:也许其他选项比推广安装更重要)。 -### Navigation menu +### 导航菜单 -Add an install button/promotion in a slide out navigation menu. +在侧边滑出导航菜单里添加安装按钮或推广信息。 ![](https://developers.google.com/web/fundamentals/app-install-banners/images/install-promo/nav.png?hl=zh-cn) -The navigation menu is a great place for promoting the installation of your app since users who open the menu are signaling engagement with your experience. +导航菜单是推广安装应用的好地方,因为打开菜单的用户会发送与你的应用互动的相关信息。 -Make sure you: +请你确保: -* Avoid disrupting important navigational content. Put the PWA install promotion below other menu items. -* Offer a short, relevant pitch for why the user would benefit from installing your PWA. +* 避免破坏重要的导航内容。把 PWA 安装推广放在其他菜单项下面。 +* 提供一个简短的相关说明,告诉用户安装 PWA 为什么能带来好处。 -### Landing page +### 落地页面 -The purpose of a landing page is to promote your products & services, so this is one place where it can be appropriate to go large with promoting the benefits of installing your PWA. +落地页的目的是宣传你的产品和服务,所以这里是一个适合大规模推广安装你们 PWA 应用优点的地方。 ![](https://developers.google.com/web/fundamentals/app-install-banners/images/install-promo/landing.png?hl=zh-cn) -First, explain your site’s value proposition, then let visitors know what they’ll get from installation. +首先,介绍你们网站的价值主张,然后让访问者了解他们能从安装中得到什么。 -Make sure you: +请你确保: -* Appeal to features that will matter most to your visitors and emphasize keywords that might have brought them to your landing page. -* This is a landing page. After you’ve made your value proposition clear, make your install promotion and call to action eye catching! -* Consider adding an install promotion within your app where users spend most of their time. +* 着重突出对你的访客最重要的功能,并强调可能将他们带到你落地页的关键字。(译者注:也许是这些关键字让用户访问的落地页) +* 既然是落地页。在明确了你的价值主张之后,应立即进行安装推广并使用号召性用语! +* 考虑在用户花费大量时间的应用上添加安装推广。 -### Install banner +### 安装 Banner -A dismissible banner at the top of the page. +页面顶部可关闭的 banner。 ![](https://developers.google.com/web/fundamentals/app-install-banners/images/install-promo/banner.png?hl=zh-cn) -Most users have encountered install banners in mobile experiences and are familiar with the interactions offered by a banner. Banners should be used carefully because they can be very disruptive to the user experience. +大多数用户在手机体验中遇到过安装 banner,并且熟悉 banner 提供的交互功能。应谨慎使用 banner,因为它们会对用户体验造成极大的破坏。 -Make sure you: +请你确保: -* Wait until the user has demonstrated interest in your site before showing a banner. If the user dismisses your banner, don’t show it again unless the user triggers a conversion event that indicates a higher level of engagement with your content such as a purchase on an eCommerce site or signing up for an account. -* Provide a brief explanation of the value of installing your PWA in the banner. For example, you can differentiate the install of a PWA from a native app by mentioning that it uses almost no storage on the user’s device or that it will install instantly without a store redirect. +* 在显示 banner 之前,请等待用户对你的站点表现出足够的兴趣。如果你的用户关闭了 banner,请不要再显示,除非用户触发了转化事件,表明和你的内容有更高的参与度,例如在电子商务网站上的消费行为或者注册成为一个账号。 +* 提供有关在 banner 中安装 PWA 的价值的简要说明。例如,你可以通过提及它的使用几乎不占用用户设备存储空间,也不会跳转到应用商店而是立即安装的方式,告诉用户安装一个 PWA 和本机应用的区别。 -## Inline promotional patterns +## 内在推广模式 -Inline promotional techniques interweave promotions with site content. This is often more subtle than promotion in application UI, which has tradeoffs. You want your promotion to stand out enough that interested users will notice it, but not so much that it detracts from the quality of your user experience. +内在推广技术将推广和站点内容交织在一起。这通常比应用 UI 上的推广更微妙一些,UI 具有权衡性。你希望你的推广足够突出,感兴趣的用户可能会注意到它,但降低了用户体验就不太值得。 -### In-feed +### 内反馈 -An in-feed install promotion appears between news articles or other lists of information cards in your PWA. +在 PWA 中的新闻文章或其他信息卡列表之间出现的一个内反馈安装推广。 ![](https://developers.google.com/web/fundamentals/app-install-banners/images/install-promo/in-feed.png?hl=zh-cn) -Your goal is to show users how to access the content they’re enjoying more conveniently. Focus on promoting features and functionality that will be helpful to your users. +你的目的是向用户展示他们如何更方便地访问到他们正在享受的内容。专注推广那些对你的用户有用的特性和功能。 -Make sure you: +请你确保: -* Limit the frequency of the promotions to avoid annoying users. -* Give your users the ability to dismiss the promotions. Remember your user’s choice to dismiss. +* 减少推广的频率以免干扰到用户。 +* 为用户提供关闭推广的能力。记住是你的用户选择去关闭 -### Booking or checkout journey +### 预定或结账流程 -Show an install promotion during or after a sequential journey, typical of booking or checkout flows. If you’re displaying the promotion after the user has completed the journey, you can often make it more prominent since the journey is completed. +在一系列流程进行或结束时展示安装推广,典型的是预定或结账流程。如果你在用户完成了流程操作时显示推广,那么因为流程已经结束你可以让它更加突出。 ![](https://developers.google.com/web/fundamentals/app-install-banners/images/install-promo/journey.png?hl=zh-cn) -Make sure you: +请你确保: -* Include a relevant call to action. Which users will benefit from installing your app and why? How is it relevant to the journey they are currently undertaking? -* If your brand has unique offers for installed app users, be sure to mention them here. -* Keep the promotion out of the way of next steps in your journey or you can negatively affect your journey completion rates. In the eCommerce example above, notice how the key journey call-to-action to checkout is above the app install promotion. +* 包含一个相关的号召性用语。哪些用户可以从安装你的应用中受益?为什么?它与用户当下的操作流程有什么关系? +* 如果你的品牌为安装了应用的用户提供独特优惠,请务必在此处提及。 +* 保证你的推广和流程的下一步是分离的,不然会对你的流程完成率造成负面影响。在上面的电子商务例子中,请注意关键结账流程操作是在应用安装推广之上的。 -### Sign up, sign in, or sign out flow - -This promotion is a special case of the [journey](#journey) promotional pattern where the promotion card can be a more prominent. +### 注册,登录或注销流程 +这类推广是前文[流程](#journey)推广模式的一种特例,并且促销卡可能会是一个更好的例子。 ![](https://developers.google.com/web/fundamentals/app-install-banners/images/install-promo/sign-up.png?hl=zh-cn) -These pages are usually only viewed by engaged users, where the value proposition of your PWA has already been established. There’s also often not a lot of other useful content to place on these pages. As a result, it’s less disruptive to make a larger call-to-action as long as it’s not in the way. +这些页面通常只会被参与的用户看到,其中你的 PWA 价值主张已经建立了。在这些页面上通常也不会放置很多其他有用的信息。所以,只要不干扰正常操作,较大规模的宣传性用语也只会产生较少的破坏性。 -Make sure you: +请你确保: -* Avoid disrupting the user’s journey inside the sign up form. If it’s a multi-step process, you might want to wait until the user has completed the journey. -* Promote features most relevant to a signed up user. -* Consider adding additional install promotion within the signed-in areas of your app. +* 避免在注册表单里破坏用户的操作流程。如果这是一个多步骤过程,你可能需要等到用户完成流程再展示。 +* 向已注册用户推广最相关的功能。 +* 考虑在你的应用登录区域添加其他的安装推广。 -## What patterns should I use? +## 我应该使用什么模式? -### eCommerce +### 电子商户 -Many eCommerce brands have a core group of loyal customers. These customers want push notifications for early access to new collections and to know when their items have shipped. They want the app on their home screen for quick access to the catalog and a full screen experience. +许多电子商务品牌都拥有一批忠实的客户。这些客户想要推送通知,以便及早访问了解新的库存并且知道他们的商品什么时候发货。他们希望在主屏幕上使用应用,以便快速访问目录和全屏体验。 -Patterns that work well for eCommerce PWAs include: +适用于电子商务PWA的模式包括: -* [Banner](#banner) -* [Header](#header) -* [Nav](#nav) -* [Landing](#landing) -* [In-feed](#in-feed) -* [Journey](#journey) -* [Sign-up](#sign-up) +* [Banner 模式](#banner) +* [标题模式](#header) +* [导航模式](#nav) +* [落地页模式](#landing) +* [内反馈模式](#in-feed) +* [流程模式](#journey) +* [注册模式](#sign-up) -#### Product Listing Page (PLP) or Category page +#### 产品列表页 (PLP) 或者类别页面 -This is a special case of the in-feed install promotional pattern, where the feed is products or category listings. +这是内反馈安装推广模式的一个特例,这里的反馈是产品或类别列表。 ![](https://developers.google.com/web/fundamentals/app-install-banners/images/install-promo/plp.png?hl=zh-cn) -Make sure you: +请你确保: -* Match the look and feel of the rest of your product listing page. -* Don’t disrupt the user’s product selection process. +* 匹配产品列表页面其余部分的外观。 +* 不干扰用户选择产品的选择过程。 -### Rich media and communications +### 富媒体和通讯 -Are you building the next social phenomenon or music streaming app? When new users visit your PWA for the first time, inviting them to install your PWA is a great way to bring them back. With less storage usage than a typical native app, your users can install your PWA and see whether your product is right for them. +你是在构建下一个社交现象级或者音乐流媒应用吗?当用户第一次访问你的 PWA 应用时,邀请他们安装你的 PWA 是一种让他们再次回来的很好的方式。由于使用比本地应用更少的存储空间,你的用户会安装你的 PWA 并看看你的产品是否适合他们。 -Patterns that work well for rich media and communications PWAs include: +适用于富媒体和通信 PWA 的模式包括: -* [Banner](#banner) -* [Header](#header) -* [Nav](#nav) -* [Landing](#landing) -* [Journey](#journey) -* [Sign-up](#sign-up) +* [Banner 模式](#banner) +* [标题模式](#header) +* [导航模式](#nav) +* [落地页模式](#landing) +* [流程模式](#journey) +* [注册模式](#sign-up) -### News +### 新闻 -If you work on a content oriented site, chances are you have regular users who would be interested in installing your PWA. +如果你在面向内容的站点上工作,那么你可能会有对安装 PWA 感兴趣的常规用户。 -Patterns that work well for news and social PWAs include: +适用于新闻和社交 PWA 的模式包括: -* [Banner](#banner) -* [Header](#header) -* [Nav](#nav) -* [Landing](#landing) -* [In-feed](#in-feed) -* [Journey](#journey) -* [Sign-up](#sign-up) +* [Banner 模式](#banner) +* [标题模式](#header) +* [导航模式](#nav) +* [落地页模式](#landing) +* [内反馈模式](#in-feed) +* [流程模式](#journey) +* [注册模式](#sign-up) -### Games +### 游戏 -The modern web is a great distribution platform for games with the biggest reach in the world. +现代网络对游戏来说是最伟大的分销平台,可以让最大型的游戏抵达世界各地。 -Patterns that work well for PWA games include: +适用于 PWA 游戏的模式包括: -* [Banner](#banner) -* [Header](#header) -* [Nav](#nav) -* [Landing](#landing) -* [In-feed](#in-feed) -* [Journey](#journey) -* [Sign-up](#sign-up) +* [Banner 模式](#banner) +* [标题模式](#header) +* [导航模式](#nav) +* [落地页模式](#landing) +* [内反馈模式](#in-feed) +* [流程模式](#journey) +* [注册模式](#sign-up) -#### End of game +#### 游戏结束 -This is really just a special case of the [inline journey](#journey) UI pattern. +这实际上就是[流程](#journey) UI 模式的一个特例。 ![](https://developers.google.com/web/fundamentals/app-install-banners/images/install-promo/game-over.png?hl=zh-cn) -Most casual and hyper casual games end quickly. If your users are enjoying the game, this is a great chance to invite them to install. - +大多数休闲和超级休闲游戏结束的很快。如果你的用户正在玩游戏,那么这是一个邀请他们安装的好机会。 > 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 --- From 4ee337bcda7a6c7ae18eaebc94e472fec35ef71f Mon Sep 17 00:00:00 2001 From: YueYong Date: Fri, 12 Jul 2019 11:05:48 +0800 Subject: [PATCH 22/68] =?UTF-8?q?=E4=BD=BF=E7=94=A8=20SVG=20=E5=92=8C=20Vu?= =?UTF-8?q?e.Js=20=E6=9E=84=E5=BB=BA=E5=8A=A8=E6=80=81=E6=A0=91=E5=9B=BE?= =?UTF-8?q?=20(#6075)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 使用 SVG 和 Vue.Js 构建动态树图 * Update building-a-dynamic-tree-diagram-with-svg-and-vue-js.md * Update building-a-dynamic-tree-diagram-with-svg-and-vue-js.md * Update building-a-dynamic-tree-diagram-with-svg-and-vue-js.md * Update building-a-dynamic-tree-diagram-with-svg-and-vue-js.md * Update building-a-dynamic-tree-diagram-with-svg-and-vue-js.md * 格式问题修正 --- ...ynamic-tree-diagram-with-svg-and-vue-js.md | 242 +++++++++--------- 1 file changed, 118 insertions(+), 124 deletions(-) diff --git a/TODO1/building-a-dynamic-tree-diagram-with-svg-and-vue-js.md b/TODO1/building-a-dynamic-tree-diagram-with-svg-and-vue-js.md index 12a5e84ef64..c8298bd376f 100644 --- a/TODO1/building-a-dynamic-tree-diagram-with-svg-and-vue-js.md +++ b/TODO1/building-a-dynamic-tree-diagram-with-svg-and-vue-js.md @@ -2,108 +2,108 @@ > * 原文作者:[Krutie Patel](https://medium.com/@krutie) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/building-a-dynamic-tree-diagram-with-svg-and-vue-js.md](https://github.com/xitu/gold-miner/blob/master/TODO1/building-a-dynamic-tree-diagram-with-svg-and-vue-js.md) -> * 译者: -> * 校对者: +> * 译者:[YueYong](https://github.com/YueYongDev) +> * 校对者:[Moonliujk](https://github.com/Moonliujk),[shixi-li](https://github.com/shixi-li) -# Building a Dynamic Tree Diagram with SVG and Vue.Js +# 使用 SVG 和 Vue.Js 构建动态树图 -This article will take you through my learning journey into how I created a dynamic tree diagram that uses SVG (Scalable Vector Graphics) for drawing Cubic Bezier curve paths and Vue.js for data reactivity. +本文将会带你了解到我是如何创建一个动态树图的,该图使用 SVG(可缩放矢量图形)绘制三次贝塞尔曲线(Cubic Bezier)路径并通过 Vue.js 以实现数据响应。 -Before we start, [take a look at the demo](http://svg-tree-diagram.surge.sh). +在开始前,[先让我们来看一个 demo](http://svg-tree-diagram.surge.sh)。 ![](https://cdn-images-1.medium.com/max/2242/1*i9yyyuT1hxMj1K7ZGP4vDg.png) -Combining the power of SVG and Vue.js framework allows creating diagrams and infographics that are data-driven, interactive and configurable. +基于 SVG 和 Vue.js 框架的强大功能,我们可以轻松创建基于数据驱动、可交互和可配置的图表与信息图。 -This diagram is a collection of Cubic Bezier curves that start from a single point, but end on different points — at equal distance — based on user-provided data. So, the diagram is reactive to user input. +该图是一个三次贝塞尔曲线的集合,它基于用户提供的数据,从单点出发,并在不同的点结束,且点和点之间的距离相同。 因此,该图会响应用户输入的内容。 -We’ll start by learning how the Cubic Bezier curve is formed and then try to find `x` and `y` points in the coordinate system that’s available within `` element followed by clipping mask. +我们将首先学习如何制作三次贝塞尔曲线,然后通过剪切蒙版在坐标系中尝试找到 `` 元素可用的 `x` 和 `y` 点。 -I’ve used plenty of visuals on a way to keep things interesting. The key idea of this article is to help you come up with your own diagram concepts for similar projects. +我在这个案例中使用了很多视觉动画以保证趣味性。本文的主要思想是帮助你为类似的项目设计出自己的图表。 ## SVG -#### How the Cubic Bezier curve is formed? +#### Cubic Bezier 曲线是如何形成的? -The type of curve you saw in the demo above, is called a Cubic Bezier curve. I have highlighted each part of this curve structure below. +你在上面的 demo 中看到的曲线被称为三次贝塞尔曲线。我已在下面高亮显示了此曲线结构的每个部分。 ![](https://cdn-images-1.medium.com/max/3960/1*GPp1gpDRFC-Xx9z7Tg85iQ.png) -It has total 4 pairs of coordinates. First pair — `(x0, y0)`— is the starting anchor point, and last pair — `(x3, y3)` — is ending anchor points that indicates where to finish the path. +它总共有 4 对坐标。第一对坐标 —— `(x0, y0)` —— 是起始锚点,最后一对坐标 —— `(x3, y3)` —— 是结束锚点,指示完成路径的位置。 -Two pairs in the middle are, +中间的两对坐标是: -* Bezier control point #1 `(x1, y1)` and -* Bezier control point #2 `(x2, y2)` +* 贝塞尔控制点 #1 `(x1, y1)` 和 +* 贝塞尔控制点 #2 `(x2, y2)` -And they’re responsible to achieve that smooth curve you see in a path. Without these control points, this path would just be a straight diagonal path! +基于这些点实现的路径是一条平滑曲线。如果没有这些控制点,这条路径就是一条笔直的线! -Let’s put these four coordinates into SVG `` syntax. +让我们把这四个坐标放入 SVG 语法的 `` 元素中。 ``` -// Cubic Bezier path syntax +// 三次贝塞尔曲线的路径语法 ``` -The letter `c` seen in the syntax, stands for Cubic Bezier curve. Small `c` indicates relative values, while the capital `C` indicates absolute value. I have used absolute value — `C` — to create this diagram. +语法中的字母 `c` 代表三次贝塞尔曲线。小 `c` 表示相对值,而大写 `C` 表示绝对值。我用绝对值 `C` 来创建这个图。 -**Achieving Symmetry** +**实现对称性** -Symmetry is the key aspect of this diagram. And to achieve that, I have used just one variable to derive values like height, width and mid-point. +对称性是实现该图的关键点。为了实现这一点,我只使用一个变量来派生出类似于高度,宽度和中点等值。 -Let’s call this variable, a `size`. Since the orientation of this tree-diagram is horizontal, this `size` variable can be viewed as an entire **horizontal** space that’s available for the diagram. +就让我们把这个变量命名为 `size` 吧。由于此树形图的方向是水平的,因此可以将变量 `size` 视为整张图的**水平**空间。 -Let’s assign most realistic value to this variable. So that, you can also calculate the coordinates along the way. +让我们为这个变量赋予实际值。这样,你还可以计算路径的坐标。 ``` size = 1000 ``` -## Finding Coordinates +## 寻找坐标 -Before we can find coordinates, we need a coordinate system in place! +在我们寻找坐标前,我们需要新建一个坐标系! -#### Coordinate system & viewBox +#### 坐标系和 viewBox -`viewBox` attribute of an \ element is an important, because it defines a user coordinate system of an SVG. In simplified terms, `viewBox` defines the position and dimension of the user-space to draw an SVG. +`` 元素的 `viewBox` 属性非常重要,因为它定义了 SVG 的用户坐标系。简而言之,`viewBox` 定义了用户空间的位置和维度以便于绘制 SVG。 -`viewBox` is made up of four numbers in this exact same order — `min-x, min-y, width, height`. +`viewBox` 由四个数字组成,顺序需要保持一致 —— `min-x, min-y, width, height`。 ``` ... ``` -The `size` variable we defined earlier will control the `width` and `height` of this coordinate system. +我们之前定义的 `size` 变量将控制此坐标系的 `width` 和 `height`。 -Later in Vue.js section, `viewBox` will be bound to computed property to populate the `width` and `height`, while `min-x` and `min-y` will always be zero in this instance. +稍后在 Vue.js 部分,`viewBox` 将绑定到计算属性以填充 `width` 和 `height`,而 `min-x` 和 `min-y` 在此实例中始终为零。 -Notice that, we’re not using `height` and `width` attributes of **SVG element** itself. Because, we’ll set `width: 100%` and `height: 100%` of an `` via CSS to make the viewport fluid. +请注意,我们没有使用 **SVG 元素**本身的 `width` 和 `height` 属性。因为,我们稍后会通过 CSS 设置 `` 的 `width: 100%` 和 `height: 100%`,以便自适应填满整个 viewport。 -Now that the user-space/coordinate system is ready for the diagram, let’s see how `size` helps calculating coordinates just by using different `%` values. +现在整张图的用户空间 / 坐标系已准备好,让我们看看 `size` 变量如何通过使用不同的 `%` 值来帮助计算坐标。 -#### Constant & Dynamic Coordinates +#### 恒定和动态坐标 ![Diagram Concept](https://cdn-images-1.medium.com/max/5184/1*2CRePTNtiym2q7eJKxEUWQ.png) -Circle is part of the diagram. That’s why, it’s important to include it in the calculations from the beginning. As portrayed in the diagram above, let’s start deriving coordinate values for a **circle** and **one sample path.** +圆是图的一部分。这就是为什么从一开始就把它包含在计算中是很重要的。如上图所示,让我们开始导出一个**圆**和**一个样本路径**的坐标值。 -**The vertical height is divided into two parts, `topHeight` (20% of `size`) and `bottomHeight` (remaining 80% of `size`). And the horizontal width is divided into two parts — 50% of `size`.** +**垂直高度分为两部分:`topHeight`(`size` 的 20%)和 `bottomHeight`(`size` 剩余的 80%)。水平宽度分为两部分 —— 分别是 `size` 的 50%**。 -This makes the circle coordinates pretty self-explanatory, `(halfSize, topHeight)`. Circle `radius` is set to half of `topHeight`, so that it fits nicely in available space. +这样圆坐标(`halfSize, topHeight`)就显而易见了。圆的 `radius` 属性设置为 `topHeight` 的一半,这样的可用空间非常合适。 -Now, let’s look at the path coordinates… +现在,让我们看一下路径坐标…… -* **`x0, y0`**— First pair of anchor points that **stays constant** at all the times. Here, `x0` is the centre of the diagram `size` and `y0` is vertical point where the circle stops **(…hence the** addition **of a radius)** and path begins. -= `(50% of size, 20% of size + radius)` -* **`x1, y1`**— Bezier control point one, which **also stays constant** for all paths. Keeping symmetry in mind, `x1` and `y1` are always half of the diagram `size`. -= `(50% of size, 50% of size)` -* **`x2, y2`**— Bezier control point two, where **`x2`** directs which side to form the curve and **is calculated dynamically** for each paths. And again, `y2` will be half the diagram `size`. -= `(x2, 50% of size)` -* **`x3, y3`**- Final pair of anchor points that indicates where to stop drawing the path. Here, `x3` imitates the value of `x2`, which is to be calculated dynamically. And `y3` takes 80% of the `size`. -= `(x3, 80% of size)` +* **`x0, y0`** —— 第一对锚点**始终保持不变**。这里,`x0` 是图表 `size` 的中心,`y0` 是圆圈停止的垂直点(**因此增加了一个 radius**)并且是路径的起点。 +=`(50% 的 size, 20% 的 size + radius)` +* **`x1, y1`** —— 贝塞尔控制点 1,对于所有路径**也保持不变**。考虑到对称性,`x1` 和 `y1` 总是图表 `size` 的一半。 += `(50% 的 size, 50% 的 size)` +* **`x2, y2`** —— 贝塞尔控制点 2,其中 `x2` 指示哪一侧形成曲线并且为每条路径**动态计算**。同样,`y2` 是图表 `size` 的一半。 += `(x2, 50% 的 size)` +* **`x3, y3`** —— 最后一对锚点,指示路径绘制结束的位置。这里,`x3` 模仿 `x2` 的值,这是动态计算的。`y3` 占据了 `size` 的 80%。 += `(x3, 80% 的 size)` -See the generic path syntax below after punching in these calculations. To represent `%`, I’ve simply divided the `%` value by 100. +在合并上述计算结果后,请参阅下面的通用路径语法。为了表示 `%`,我只是简单的将 `%` 值除以 100。 ``` ` tag of `` element as a content, I have bound image wi ``` -Since we’re trying to fit square image into a circle, I’ve adjusted the image position by reducing the `radius` of a circle to achieve full visibility of an image through the circular mask. +由于我们试图将方形图像拟合成圆形,我通过减小圆的 `radius` 来调整图像位置,以通过圆形蒙版实现图像的完全可见性。 -Let’s put together all the values visually in a diagram to help us see the bigger picture. +让我们将所有的值都放入图表中,以帮助我们看到完整的图像。 ![](https://cdn-images-1.medium.com/max/3752/1*kWPi7xIsu6PF9drIwOKo4Q.png) -## Dynamic SVG using Vue.js +## 使用 Vue.js 的动态 SVG -So far we have understood how Bezier curve works and surrounding nitty gritty. As a result, we have a static SVG diagram concept. Using Vue.js with SVG, we’ll now make this diagram data-driven and transform it from static to dynamic. +到目前为止,我们已经了解了贝塞尔曲线的本质,以及它的工作原理。因此,我们有了静态 SVG 图的概念。使用 Vue.js 和 SVG,我们现在将用数据驱动图表,并将其从静态转换为动态。 -In this section we will break down SVG diagram into Vue components and bind SVG attributes to computed properties and make it respond to data changes. +在本节中,我们将把 SVG 图分解为 Vue 组件,并将 SVG 属性绑定到计算属性,并使其响应数据更改。 -Finally, we will also look at a config panel component which is used to provide data to the dynamic SVG diagram. +最后,我们还将查看配置面板组件,该组件用于向动态 SVG 图提供数据。 -We’ll learn about the following key topics in this section. +我们将在本节中了解以下关键主题。 -* Binding SVG viewBox -* Calculating SVG Path Coordinates -* Two options for Bezier curve path implementation -* Config Panel -* Homework ❤ +- 绑定 SVG viewBox +- 计算 SVG 路径坐标 +- 实现贝塞尔曲线路径的两个选项 +- 配置面板 +- 家庭作业 ❤ -**Binding SVG viewBox** +**绑定 SVG viewBox** -First and foremost, we need a coordinate system in place to be able to draw inside SVG. And computed property `viewbox` will do just that using `size` variable. It contains four values separated by a whitespace — which is fed into **`viewBox`** attribute of an `` element. +首先,我们需要一个坐标系统才能在 SVG 内部绘制。 计算属性 `viewbox` 将使用 `size` 变量。它包含由空格分隔的四个值 —— 它被送入 `` 元素的 **`viewBox`** 属性。 ``` viewbox() @@ -216,54 +216,48 @@ viewbox() } ``` -In SVG, `viewBox` attribute is written in camelCase **already**. +在 SVG 中,`viewBox` 属性**已经**使用驼峰命名法(camelCase)。 ``` ``` -Therefore, to correctly bind it to a computed property, I’ve used kebab-case version of attribute (below), followed by `.camel` modifier. This way HTML is tricked into binding this attribute correctly. +因此为了正确绑定上计算属性,我在 `.camel` 修饰符后对该变量使用了短横线命名(kebab-case)的方式(如下所示)。通过这种方式,HTML 才得以正确绑定此属性。 -``` - - ... - -``` - -Now, every time we change the `size`, the diagram will adjust itself without us having to change the markup manually. +现在,每次我们更改 `size` 时,图表都会自行调整,而无需手动更改标记。 -**Calculating SVG Path Coordinates** +**计算 SVG 路径坐标** -Since most values are derived from a single variable, `size`, I’ve used computed properties for all of the constant coordinates. Don’t be confused by the term constant here. These values are derived from the `size`, but after **that**, they remain constant — no matter how many curved paths are created. +由于大多数值都是从单个变量 `size` 派生的,所以我已经为所有常量坐标使用了计算属性。不要被这里的常量混淆。这些值是从 `size` 中派生出来的,但在**此**之后,无论创建多少曲线路径,它们都保持不变。 -And if you change the size of an SVG, these values are computed again. With that in mind, here’s the list of five values required to draw Bezier curves. +如果你改变 SVG 的大小,这些值会再次被计算出来。考虑到这一点,这里列出了绘制贝塞尔曲线所需的五个值。 -* topHeight— `size * 0.2` +* topHeight — `size * 0.2` * bottomHeight — `size * 0.8` * width — `size` * halfSize — `size * 0.5` -* distance— `size/arrayLength` +* distance — `size/arrayLength` -At this point, we are left with two unknown values, `x2` and `x3` — and we do have a formula to find their values. +此时,我们只剩下两个未知值,即 `x2` 和 `x3`,我们有一个公式可以确定它们的值。 ``` x = index * distance + (distance * 0.5) ``` -To find `x` above, we need to feed `index` into the formula for each path at once. So… +为了找到上面的 `x`,我们需要一次将 `index` 输入到每个路径的公式中。所以…… -Is computed property an appropriate option here? The short answer is no. +在这使用计算属性合适吗?肯定不合适。 -We cannot pass a parameter to a computed property — because it’s a property, and not a function. Also, needing a parameter to calculate something means — there’s no clear caching benefit of using the computed property anyways. +我们不能将参数传递给计算属性 —— 因为它是一个属性,而不是函数。另外,需要一个参数来计算意味着——使用计算属性对缓存也没什么好处。 -****Note:** There’s an exception to above. Vuex. If we’re using Vuex Getters, then yes, we can pass parameter to getters by returning a function.** +**注意:上面有一个例外,Vuex。如果我们正在使用 Vuex Getters,那么,我们可以通过返回一个函数将参数传递给 getter。** -We’re not using Vuex in this instance. Even then, we’re left with couple of options. +在本文所述的情况下,我们不使用 Vuex。可即便如此,我们仍有两个选择。 -#### Option 1 +#### 选择一 -We can define a function, where we pass an array `index` as an argument and return the result. Bit cleaner option, if we’re to use this value at multiple places in the template. +我们可以定义一个函数,在这里我们将数组 `index` 作为参数传递并返回结果。如果要在模板中的多个位置使用此值,选择 `Bit cleaner`。 ``` @@ -275,7 +269,7 @@ We can define a function, where we pass an array `index` as an argument and retu ``` -**`calculateXPos()`** method will evaluate every time it’s called. And this method accepts index — `i` — as an argument. (Below) +calculateXPos() 方法将在每次调用时进行评估。并且此方法接受索引 —— `i` —— 作为参数(代码如下)。 ``` ``` -Here’s the CodePen that uses **Option 1**. +下面是运行在 CodePen 上 Option 1 的结果。 [Option 1 - Bezier Curve Tree Diagram with Vue Js](https://codepen.io/krutie/pen/eoRXWP) -#### Option 2 +#### 选择二 -Better yet, we can extract this small SVG path markup into its own little child component and pass `index` as a prop into it — along with other required props, of course. +更好的是,我们可以将这个小的 SVG 路径标记提取到它自己的子组件中,并将 `index` 作为一个属性传递给它 —— 当然,还有其他必需的属性。 -In this instance, we can even use computed property to find `x2` and `x3`. +在这个例子中,我们甚至可以使用计算属性来查找 `x2` 和 `x3`。 ``` @@ -311,7 +305,7 @@ In this instance, we can even use computed property to find `x2` and `x3`. ``` -This option gives us an opportunity to be more organised with the code. For example, we can create one more child component for the clipping-mask of a circle as below. +这种方法可以让我们的代码更具条理,例如,我们可以为一个圆形剪切蒙版创建一个或多个子组件,如下所示。 ``` ``` -#### Config Panel +#### 配置面板 ![Config Panel](https://cdn-images-1.medium.com/max/2000/1*zI1UlqRzNrxoGQdgl9nCSA.png) -As you may have already seen the **Config Panel** on top-left corner in CodePen above, it facilitates adding and removing of items from array. Following Option 2, I have created a child component to accommodate the Config Panel as well, which leaves top-level Vue component clean and readable. As a result, sweet little Vue component tree will look something like below. +您可能已经在 CodePen 左上角看到了 **控制面板**。它可以添加和删除数组中的元素。在 Option 2 中,我创建了一个子组件来容纳 Config Panel,使顶级 Vue 组件清晰可读。我们的 Vue 组件树看起来就像下面这样。 ![](https://cdn-images-1.medium.com/max/2942/1*ztoHw3dN6o_0VvwI1UOpxw.png) -Wondering how the code would look like now? Here’s the CodePen using **Option 2**. +想知道 Option 2 的代码是什么样子的?下面的链接是在 CodePen 上使用了 Option 2 的代码。 [Option 2 - Bezier Curve Tree Diagram with Vue Js](https://codepen.io/krutie/pen/Bexoez) -## GitHub Repo +## GitHub 仓库 -Finally, here’s the [GitHub Repo](https://github.com/Krutie/svg-tree-diagram) for you to review the project (with Option 2) before you move onto the next section. +最后,这里有一个为你准备的 [GitHub Repo](https://github.com/Krutie/svg-tree-diagram),你可以在进入下一部分之前查看该项目(使用选项 2)。 -## Homework +## 家庭作业 -Try creating the same diagram in vertical mode based on a logic presented in this article. +尝试基于本文中介绍的逻辑在垂直模式下创建相同的图表。 -If you think, it could be as easy as swapping the x and y coordinates, then you’re correct! Because the hard-work is done already, after swapping the coordinates **where required**, update the code with appropriate variables and method names. +如果你认为,它是交换坐标系中的 `x` 值和 `y` 值一样简单的话,那么你是对的!因为最艰难的部分已经完成,在交换了**所需**的坐标后,再用适当的变量和方法更新代码。 -With the help of Vue.js, this diagram can be extended further with more features, such as, +在 Vue.js 的帮助下,该图可以通过更多功能进一步扩展,例如, -* Create a switch to toggle between horizontal and vertical mode -* Maybe use GSAP to animate the path -* Control path attributes (such as colour & stroke width) from config panel -* Use external library to save and download the diagram as an image/PDF +* 创建一个开关以便于在水平和垂直模式之间切换 +* 可以使用 GSAP 为路径设置动画 +* 从配置面板控制路径属性(例如颜色和笔触宽度) +* 使用第三方工具库将图表保存并下载为图像/PDF -Now give it a try and if needed, there’s the answer-link to the Homework below. +现在试一试,如果需要的话,下面是家庭作业的答案链接。 -Good Luck! +祝你好运! -## Conclusion +## 总结 -`` is one of the many powerful elements in SVG, as it allows you to create graphics and diagrams with precision. In this article, we understood how Bezier curve works and its application to create custom diagram. +`` 是 SVG 中众多强大的元素之一,因为它允许你精确地创建图形和图表。在本文中,我们了解了贝塞尔曲线的工作原理以及如何创建一个自定义图表应用。 -It’s always daunting to adjust with data-driven approach used by modern JavaScript frameworks, but Vue.js makes it really easy, and also takes care of mundane tasks such as DOM manipulation. So that you — as a developer — can think in terms of data even while working with projects that are significantly visual in nature. +利用现代 JavaScript 框架所使用的数据驱动方法进行调整总是令人生畏的,但 Vue.js 使它变得非常简单,并且还可以处理诸如 DOM 操作之类的简单任务。因此,作为一名开发人员,即使在处理具有明显视觉效果的项目时,你也可以用数据的方式进行思考。 -I’ve realised that it took some of the simple concepts of both Vue.js and SVG to create this diagram that seems complex by the look of it. I encourage you to read about [Building an Interactive Infographic with Vue.js](https://www.smashingmagazine.com/2018/11/interactive-infographic-vue-js/), if you haven’t read already. It’d be good read after this one. ❤ And here’s an [answer](https://codepen.io/krutie/pen/QRrNKz) to the homework. +我已经意识到创建这个看起来很复杂的图表需要 Vue.js 和 SVG 的一些简单概念。如果你还没有准备好,我建议您阅读有关[使用 Vue.js 构建交互式信息图](https://www.smashingmagazine.com/2018/11/interactive-infographic-vue-js/)的内容。读完那篇文章后再回过头阅读本文就会容易很多。❤这是家庭作业的[答案](https://codepen.io/krutie/pen/QRrNKz)。 -I hope you have learned something from this article and had as much fun reading it as I had while creating it. +我希望你从这篇文章中学到了一些东西,并在阅读本文时能够感受到我当时创作时的乐趣。 > 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 From 8a8a02be48ec93cbfaf84755ab3a0b20cc2ede09 Mon Sep 17 00:00:00 2001 From: cc Jia <174676928@qq.com> Date: Fri, 12 Jul 2019 11:08:07 +0800 Subject: [PATCH 23/68] =?UTF-8?q?=E5=9C=A8=20Python=20=E4=B8=AD=E8=BF=87?= =?UTF-8?q?=E5=BA=A6=E4=BD=BF=E7=94=A8=E5=88=97=E8=A1=A8=E6=8E=A8=E5=AF=BC?= =?UTF-8?q?=E5=BC=8F=E5=92=8C=E8=A1=A8=E8=BE=BE=E5=BC=8F=E7=94=9F=E6=88=90?= =?UTF-8?q?=E5=99=A8=20(#6059)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 在 Python 中过度使用列表解析器和生成表达式 在 Python 中过度使用列表解析器和生成表达式 * Update abusing-and-overusing-list-comprehensions-in-python.md JalanJiang 校对 完成 * Update TODO1/abusing-and-overusing-list-comprehensions-in-python.md Co-Authored-By: TrWestdoor * Update TODO1/abusing-and-overusing-list-comprehensions-in-python.md Co-Authored-By: TrWestdoor * Update TODO1/abusing-and-overusing-list-comprehensions-in-python.md Co-Authored-By: TrWestdoor * Update TODO1/abusing-and-overusing-list-comprehensions-in-python.md Co-Authored-By: TrWestdoor * Update TODO1/abusing-and-overusing-list-comprehensions-in-python.md Co-Authored-By: TrWestdoor * Update TODO1/abusing-and-overusing-list-comprehensions-in-python.md Co-Authored-By: TrWestdoor * Update TODO1/abusing-and-overusing-list-comprehensions-in-python.md Co-Authored-By: TrWestdoor * Update TODO1/abusing-and-overusing-list-comprehensions-in-python.md Co-Authored-By: TrWestdoor * Update TODO1/abusing-and-overusing-list-comprehensions-in-python.md Co-Authored-By: TrWestdoor * Update TODO1/abusing-and-overusing-list-comprehensions-in-python.md Co-Authored-By: TrWestdoor * Update TODO1/abusing-and-overusing-list-comprehensions-in-python.md Co-Authored-By: TrWestdoor * Update TODO1/abusing-and-overusing-list-comprehensions-in-python.md Co-Authored-By: TrWestdoor * Update TODO1/abusing-and-overusing-list-comprehensions-in-python.md Co-Authored-By: TrWestdoor * Update abusing-and-overusing-list-comprehensions-in-python.md * Update abusing-and-overusing-list-comprehensions-in-python.md * Update abusing-and-overusing-list-comprehensions-in-python.md --- ...overusing-list-comprehensions-in-python.md | 187 +++++++++--------- 1 file changed, 94 insertions(+), 93 deletions(-) diff --git a/TODO1/abusing-and-overusing-list-comprehensions-in-python.md b/TODO1/abusing-and-overusing-list-comprehensions-in-python.md index c3cca0b1fb2..246379729de 100644 --- a/TODO1/abusing-and-overusing-list-comprehensions-in-python.md +++ b/TODO1/abusing-and-overusing-list-comprehensions-in-python.md @@ -2,38 +2,38 @@ > * 原文作者:[Trey Hunner](https://treyhunner.com/) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/abusing-and-overusing-list-comprehensions-in-python.md](https://github.com/xitu/gold-miner/blob/master/TODO1/abusing-and-overusing-list-comprehensions-in-python.md) -> * 译者: -> * 校对者: +> * 译者:[ccJia](https://github.com/ccJia) +> * 校对者:[江五渣](http://jalan.space),[TrWestdoor](https://github.com/TrWestdoor) -# Overusing list comprehensions and generator expressions in Python +# 列表推导式与表达式生成器在 Python 中的滥用 -List comprehensions are one of my favorite features in Python. I love list comprehensions so much that I’ve written an [article](https://treyhunner.com/2015/12/python-list-comprehensions-now-in-color/ "List Comprehensions: Explain Visually") about them, done [a talk](https://youtu.be/5_cJIcgM7rw "Comprehensible Comprehensions") about them, and held a [3 hour comprehensions tutorial](https://youtu.be/_6U1XoxyyBY "Using List Comprehensions and Generator Expressions For Data Processing") at PyCon 2018. +列表推导式是我喜欢的 Python 特性之一。我非常喜爱列表推导式,为此我写过一篇关于它们的[文章](https://treyhunner.com/2015/12/python-list-comprehensions-now-in-color/ "List Comprehensions: Explain Visually"),做过一次针对它们的[演讲](https://youtu.be/5_cJIcgM7rw "Comprehensible Comprehensions"),还在 PyCon 2018 上办过一个[三小时推导式教程](https://youtu.be/_6U1XoxyyBY "Using List Comprehensions and Generator Expressions For Data Processing")。 -While I love list comprehensions, I’ve found that once new Pythonistas start to really appreciate comprehensions they tend to use them everywhere. **Comprehensions are lovely, but they can easily be overused**! +我喜爱推导式,但是我发现一旦一个新的 Python 使用者开始真正使用推导式,他们会在所有可能的地方用这些推导式。**推导式很可爱,但也很容易被滥用**。 -This article is all about cases when comprehensions aren’t the best tool for the job, at least in terms of readability. We’re going to walk through a number of cases where there’s a more readable alternative to comprehensions and we’ll also see some not-so-obvious cases where comprehensions aren’t needed at all. +这篇文章展示的案例中,从可读性的角度来看,推导式都不是完成任务的最佳工具。我们会讨论一些案例,它们有比使用推导式更具有可读性的选择,我们还会看到一些不明显的案例,它们根本就不需要使用推导式。 -This article isn’t meant to scare you off from comprehensions if you’re not already a fan; it’s meant to encourage moderation for those of us (myself included) who need it. +如果你还不是推导式的爱好者,那么这篇文章并不是为了吓退你,而是为了鼓励那些需要它的人(包括我)适度地使用它。 -**Note**: In this article, I’ll be using the term “comprehension” to refer to all forms of comprehensions (list, set, dict) as well as generator expressions. If you’re unfamiliar with comprehensions, I recommend reading [this article](https://treyhunner.com/2015/12/python-list-comprehensions-now-in-color/ "List Comprehensions: Explain Visually") or watching [this talk](https://youtu.be/5_cJIcgM7rw "Comprehensible Comprehensions") (the talk dives into generator expressions a bit more deeply). +**注意**:本文中涉及到的“推导式”是涵盖了所有形式的推导式(列表,集合,字典)以及生成表达式。如果你对推导式还不是特别熟悉,我建议你先阅读这篇[文章](https://treyhunner.com/2015/12/python-list-comprehensions-now-in-color/ "List Comprehensions: Explain Visually") 或者这个[演讲](https://youtu.be/5_cJIcgM7rw "Comprehensible Comprehensions")(这个演讲对生成器表达式挖掘的比较深)。 -## Writing comprehensions with poor spacing +## 编写拥挤的推导式 -Critics of list comprehensions often say they’re hard to read. And they’re right, many comprehensions **are** hard to read. **Sometimes all a comprehension needs to be more readable is better spacing**. +列表推导式的批评者总是抱怨它们的可读性太差。他们是对的,很多推导式**都**很难读。**一些时候,让这些推导式变的更易读的方法仅仅是多一点间隔**。 -Take the comprehension in this function: +观察一下这个函数中的推导式: ```python def get_factors(dividend): - """Return a list of all factors of the given number.""" + """返回所给数值的所有因子作为一个列表。""" return [n for n in range(1, dividend+1) if dividend % n == 0] ``` -We could make that comprehension more readable by adding some well-placed line breaks: +我们可以通过添加一些合适的换行来让这个推导式更易读: ```python def get_factors(dividend): - """Return a list of all factors of the given number.""" + """返回所给数值的所有因子作为一个列表。""" return [ n for n in range(1, dividend+1) @@ -41,15 +41,15 @@ def get_factors(dividend): ] ``` -Less code can mean more readable code, but not always. **Whitespace is your friend, especially when you’re writing comprehensions**. +代码越少意味着越好的可读性,但并不总是这样。**空白符是你的好朋友,尤其是在你使用推导式的时候**。 -In general, I prefer to write most of my comprehensions **spaced out over multiple lines of code** using the indentation style above. I do write one-line comprehensions sometimes, but I don’t default to them. +通常来说,我跟倾向于使用上面的缩进格式来写我的推导式并**利用多行来隔离代码**。有时我也用单行来写解析式,但是我不默认使用单行。 -## Writing ugly comprehensions +## 编写的推导式太丑 -Some loops technically **can** be written as comprehensions but they have so much logic in them they probably **shouldn’t** be. +一些循环是**可以**被写成推导式的形式,但是如果循环里面有太多逻辑,那他们可能**不应该**被这样改写。 -Take this comprehension: +观察一下这个推导式: ```python fizzbuzz = [ @@ -61,7 +61,7 @@ fizzbuzz = [ ] ``` -This comprehension is equivalent to this `for` loop: +这个推导式等价于这样的 `for` 循环: ```python fizzbuzz = [] @@ -74,9 +74,9 @@ for n in range(100): ) ``` -Both the comprehension and the `for` loop use three nested [inline if statements](https://docs.python.org/3/faq/programming.html#is-there-an-equivalent-of-c-s-ternary-operator) (Python’s [ternary operator](https://en.wikipedia.org/wiki/%3F:)). +推导式和 `for` 循环都使用了三层嵌套的 [内联 if 语句](https://docs.python.org/3/faq/programming.html#is-there-an-equivalent-of-c-s-ternary-operator) (Python 的[三元操作符](https://en.wikipedia.org/wiki/%3F:)) -Here’s a more readable way to write this code, using an `if-elif-else` construct: +这里有一个更易读的方式,使用 `if-elif-else` 结构: ```python fizzbuzz = [] @@ -91,9 +91,9 @@ for n in range(100): fizzbuzz.append(n) ``` -Just because there **is** a way to write your code as a comprehension, **that doesn’t mean that you **should** write your code as a comprehension**. +即使这里**有**一种用推导式书写代码的方法,**但是这并不意味着你**必须**要这么做**。 -Be careful using any amount of complex logic in comprehensions, even a single [inline if](https://docs.python.org/3/faq/programming.html#is-there-an-equivalent-of-c-s-ternary-operator): +在推导式里有很多复杂逻辑时,即使是单个的 [内联 if](https://docs.python.org/3/faq/programming.html#is-there-an-equivalent-of-c-s-ternary-operator) 也需要谨慎。 ```python number_things = [ @@ -102,7 +102,7 @@ number_things = [ ] ``` -If you really prefer to use a comprehension in cases like this, at least give some thought to **whether whitespace or parenthesis could make things more readable**: +如果你倾向于在此类案例中使用推导式,那你至少需要考虑**是否可以使用空白符或者括号可以提高可读性**: ```python number_things = [ @@ -111,7 +111,7 @@ number_things = [ ] ``` -And consider whether breaking some of your logic out into a separate function might improve readability as well (it may not in this somewhat silly example). +并且,考虑一下提取你的逻辑操作到一个独立的函数是否也可以改进你的可读性(这个略傻的例子没有体现)。 ```python number_things = [ @@ -120,21 +120,21 @@ number_things = [ ] ``` -Whether a separate function makes things more readable will depend on how important that operation is, how large it is, and how well the function name conveys the operation. +一个独立的函数是否可以提高可读性,取决于这个操作的重要程度、规模,以及函数名能否传达操作的含义。 -## Loops disguised as comprehensions +## 伪装成推导式的循环 -Sometimes you’ll encounter code that uses a comprehension syntax but breaks the spirit of what comprehensions are used for. +有时你会遇到使用了推导式语法却破坏了推导式初衷的代码。 -For example, this code looks like a comprehension: +比如,这个代码好像是一个推导式: ```python [print(n) for n in range(1, 11)] ``` -But it doesn’t **act** like a comprehension. We’re using a comprehension for a purpose it wasn’t intended for. +但是它不像推导式一样**运行**。我们使用推导式达到的目的并不是它的本意。 -If we execute this comprehension in the Python shell you’ll see what I mean: +如果我们在 Python 中执行这个推导式,你就会明白我的意思: ```python >>> [print(n) for n in range(1, 11)] @@ -142,30 +142,30 @@ If we execute this comprehension in the Python shell you’ll see what I mean: [None, None, None, None, None, None, None, None, None, None] ``` -We wanted to print out all the numbers from 1 to 10 and that’s what we did. But this comprehension statement also returned a list of `None` values to us, which we promptly discarded. +我们是想打印 1 到 10 之间的所有数,同时我们也是这么做的。但是这个推导式的语句返回了一个全是 `None` 值的列表给我们,对我们毫无意义。 -**Comprehensions build up lists: that’s what they’re for**. We built up a list of the return values from the `print` function and the `print` function returns `None`. +**你给推导式什么内容,它就会建立什么样的列表**。我们从 `print` 函数那里获得值去建立列表,而 `print` 函数的返回值就是 `None`。 -But we didn’t care about the list our comprehension built up: we only cared about its side effect. +但我们并不在意推导式建立的列表,我们只关心它的副作用。 -We could have instead written that code like this: +我们可以用下面的代码替代之前的代码: ```python for n in range(1, 11): print(n) ``` -List comprehensions are for **looping over an iterable and building up new lists**, while `for` loops are for **looping over an iterable to do pretty much any operation you’d like**. +列表推导式会**循环一个迭代器并且建立一个新的列表**,`for` 循环是用来**遍历一个迭代器同时完成你想做的任何操作**。 -When I see a list comprehension in code **I immediately assume that we’re building up a new list** (because that’s what they’re for). If you use a comprehension for **a purpose outside of building up a new list**, it’ll confuse others who read your code. +当我在代码中看到推导式时,**我立即会假设我们创建了一个新的列表**(因为这个就是它的作用)。如果你用一个推导式完成**创建列表之外的目的**,它会给其他读你代码的人带来困扰。 -If you don’t care about building up a new list, don’t use a comprehension. +如果你不是为了创建一个新的列表,那就不要使用推导式。 -## Using comprehensions when a more specific tool exists +## 当存在更特定工具时,使用推导式 -For many problems, a more specific tool makes more sense than a general purpose `for` loop. **But comprehensions aren’t always the best special-purpose tool for the job at hand.** +在很多问题中,更特定的工具比通用目的的 `for` 循环更有意义。**但推导式并不总是最适合手头工作的专用工具。** -I have both seen and written quite a bit of code that looks like this: +我见过并且写过一堆像这样的代码: ```python import csv @@ -177,9 +177,9 @@ with open('populations.csv') as csv_file: ] ``` -That comprehension is sort of an **identity** comprehension. Its only purpose is to loop over the given iterable (`csv.reader(csv_file)`) and create a list out of it. +这种推导式会对**唯一性**的值进行排序。它的目的就是循环我们提供的迭代器( `csv.reader(csv_file)` )并且创建一个列表。 -But in Python, we have a more specialized tool for this task: the `list` constructor. Python’s `list` constructor can do all the looping and list creation work for us: +但是,在 Python 中,我们为这个任务提供了一个更特定的工具:`list` 的构造函数。Python 的 `list` 构造函数可以为我们完成循环并创建列表的工作。 ```python import csv @@ -188,47 +188,47 @@ with open('populations.csv') as csv_file: lines = list(csv.reader(csv_file)) ``` -Comprehensions are a special-purpose tool for looping over an iterable to build up a new list while modifying each element along the way and/or filtering elements down. The `list` constructor is a special-purpose tool for looping over an iterable to build up a new list, without changing anything at all. +推导式是一种特殊用途的工具,用于在迭代器上循环,以便在修改每个元素的同时创建一个新列表,并/或过滤掉一些元素。`list` 构造函数是一个特定目的工具,用来遍历推导式并创建列表,同时不会改变任何的东西。 -If you don’t need to filter your elements down or map them into new elements while building up your new list, **you don’t need a comprehension: you need the `list` constructor**. +如果在建立列表时你不需要过滤元素或将它们映射到新元素中,**你不需要使用推导式,你只需要使用 `list` 构造函数**。 -This comprehension converts each of the `row` tuples we get from looping over `zip` into lists: +这个推导式转换了从 `zip` 中得到的 `row` 元组并放入列表: ```python def transpose(matrix): - """Return a transposed version of given list of lists.""" + """返回给定列表的转置版本。""" return [ [n for n in row] for row in zip(*matrix) ] ``` -We could use the `list` constructor for that too: +我们同样也可以使用 `list` 构造函数: ```python def transpose(matrix): - """Return a transposed version of given list of lists.""" + """返回给定列表的转置版本。""" return [ list(row) for row in zip(*matrix) ] ``` -Whenever you see a comprehension like this: +每当你看到如下的推导式时: ```python my_list = [x for x in some_iterable] ``` -You could write this instead: +你可以用这种写法替代: ```python my_list = list(some_iterable) ``` -The same applies for `dict` and `set` comprehensions. +这同样适用于 `dict` 和 `set` 的推导式。 -This is also something I’ve written quite a bit in the past: +这个是我过去经常会写的东西: ```python states = [ @@ -246,21 +246,21 @@ abbreviations_to_names = { } ``` -Here we’re looping over a list of two-item tuples and making a dictionary out of them. +我们遍历一个有两项元组构成的列表,并以此生成一个字典。 -This task is exactly what the `dict` constructor was made for: +这个任务实际上已经被 `dict`的构造函数完成了: ```python abbreviations_to_names = dict(states) ``` -The built-in `list` and `dict` constructors aren’t the only comprehension-replacing tools. The standard library and third-party libraries also include tools that are sometimes better suited for your looping needs than a comprehension. +`list` 和 `dict` 的构造函数不是唯一的推导式替代工具。标准库和第三方库中包含了很多工具,在有的时候,他们比推导式更适合于你的循环要求。 -Here’s a generator expression that sums up an iterable-of-iterables-of-numbers: +下面这个是一个生成器表达式,目的是对嵌套迭代器求和: ```python def sum_all(number_lists): - """Return the sum of all numbers in the given list-of-lists.""" + """返回二维列表中所有元素的和。""" return sum( n for numbers in number_lists @@ -268,27 +268,27 @@ def sum_all(number_lists): ) ``` -And here’s the same thing using `itertools.chain`: +使用 `itertools.chain` 可以达到同样的目的: ```python from itertools import chain def sum_all(number_lists): - """Return the sum of all numbers in the given list-of-lists.""" + """返回二维列表中所有元素的和。""" return sum(chain.from_iterable(number_lists)) ``` -When you should use a comprehension and when you should use the alternative isn’t always straightforward. +什么时候使用推导式什么时候使用替代品,这个的界定没有那么清晰。 -I’m often torn on whether to use `itertools.chain` or a comprehension. I usually write my code both ways and then go with the one that seems clearer. +我也经常纠结使用 `itertools.chain` 还是推导式。我通常会把两种都写出来然后使用更清晰的那个。 -Readability is fairly problem-specific with many programming constructs, comprehensions included. +可读性在编程结构中总是针对于特定问题的,这个在推导式上也适用。 -## Needless work +## 无效的工作 -Sometimes you’ll see comprehensions that shouldn’t be replaced by another construct but should instead be **removed entirely**, leaving only the iterable they loop over. +有时候你会发现,推导式不应该被另一个构造函数所替代,而应该被**完全删除**,只留下需要遍历的迭代器。 -Here we’re opening up a file of words (with one word per line), storing file in memory, and counting the number of times each occurs: +这段代码打开了一个单词构成的文件(每行一个单词),存储这个文件,同时计数每个单词出现的次数: ```python from collections import Counter @@ -299,7 +299,7 @@ word_counts = Counter( ) ``` -We’re using a generator expression here, but we don’t need to be. This works just as well: +我们使用了一个生成器表达式,但我们并不需要如此。可以直接这样写: ```python from collections import Counter @@ -307,9 +307,9 @@ from collections import Counter word_counts = Counter(open('word_list.txt').read().splitlines()) ``` -We were looping over a list to convert it to a generator before passing it to the `Counter` class. That was needless work! The `Counter` class accepts **any iterable: it doesn’t care whether they’re lists, generators, tuples, or something else**. +我们在传给 `Counter` 类之前遍历了整个列表并转换为一个生成器。完全是无用功。`Counter` 类是接受**任何迭代器,不论它是列表,生成器,元组或者是其它结构**。 -Here’s another needless comprehension: +这是另外一个无效的推导式: ```python with open('word_list.txt') as words_file: @@ -319,9 +319,9 @@ with open('word_list.txt') as words_file: print('z word', line, end='') ``` -We’re looping over `words_file`, converting it to a list of `lines`, and then looping over `lines` just once. That conversion to a list was unnecessary. +我们遍历了 `words_file`,转化为列表 `lines`,再去遍历 `lines` 一次。整个对于列表的转换是不必要的。 -We could just loop over `words_file` directly instead: +我们可以直接遍历 `words_file`: ```python with open('word_list.txt') as words_file: @@ -330,17 +330,17 @@ with open('word_list.txt') as words_file: print('z word', line, end='') ``` -There’s no reason to convert an iterable to a list if all we’re going to do is loop over it once. +没有任何理由将我们只需要遍历一次的迭代器转换为列表。 -In Python, we often care less about **whether something is a list** and more about **whether it’s an iterable**. +在 Python 中,我们更关注**它是不是一个迭代器**而不是**它是不是一个列表**。 -Be careful not to create new iterables when you don’t need to: **if you’re only going to loop over an iterable once, just use the iterable you already have**. +在不需要的时候,不要去创建一个新的迭代器。**如果你只是为了遍历这个迭代器一次,你可以直接使用它**。 -## When would I use a comprehension? +## 什么时候应该使用推导式? -So when would you actually use a comprehension? +那么,什么时候确实应该使用推导式呢? -The simple but imprecise answer is whenever you can write your code in the below [comprehension copy-pasteable format](https://treyhunner.com/2015/12/python-list-comprehensions-now-in-color/ "List Comprehensions: Explain Visually") and there isn’t another tool you’d rather use for shortening your code, you should consider using a list comprehension. +一个简单但是不准确的回答是,当你需要写如下文[复制-粘贴推导式格式](https://treyhunner.com/2015/12/python-list-comprehensions-now-in-color/ "List Comprehensions: Explain Visually")中所提到的代码,同时你没有其他的工具可以让你的代码更精简,你就应该考虑使用列表推导式了。 ```python new_things = [] @@ -349,7 +349,7 @@ for ITEM in old_things: new_things.append(some_operation_on(ITEM)) ``` -That loop can be rewritten as this comprehension: +循环可以用这样的推导式重写: ```python new_things = [ @@ -359,9 +359,9 @@ new_things = [ ] ``` -The complex answer is whenever comprehensions make sense, you should consider them. That’s not really an answer, but there is no one answer to the question “when should I use a comprehension”? +更复杂的回答是,当推导式有意义时,你就应该考虑它。这实际上不算是一个回答,但确实没人回答“什么时候该使用推导式”这个问题。 -For example here’s a `for` loop which doesn’t really look like it could be rewritten using a comprehension: +这里有一个 `for` 循环看起来的确不像是可以用推导式重写: ```python def is_prime(candidate): @@ -371,7 +371,7 @@ def is_prime(candidate): return True ``` -But there is in fact another way to write this loop using a generator expression, if we know how to use the built-in `all` function: +但实际上,如果我们知道怎么使用 `all` 函数,我们可以用生成器表达式来重写它: ```python def is_prime(candidate): @@ -381,9 +381,9 @@ def is_prime(candidate): ) ``` -I wrote [a whole article on the `any` and `all` functions](https://treyhunner.com/2016/11/check-whether-all-items-match-a-condition-in-python/) and how they pair so nicely with generator expressions. But `any` and `all` aren’t alone in their affinity for generator expressions. +我写过一篇文章叫 [ `any` 和 `all` 函数](https://treyhunner.com/2016/11/check-whether-all-items-match-a-condition-in-python/)的文章来描述这对操作和生成器表达式是多么搭配。但是 any 和 all 并不是唯一与生成器表达式有关联的。 -We have a similar situation with this code: +还有一个相似场景的代码: ```python def sum_of_squares(numbers): @@ -393,31 +393,32 @@ def sum_of_squares(numbers): return total ``` -There’s no `append` there and no new iterable being built up. But if we create a generator of squares, we could pass them to the built-in `sum` function to get the same result: +这里没有 `append` 同时也没有迭代器被建立。但是,如果我们创建一个平方的生成器,我们可以使用内置的 `sum` 函数去得到一样的结果。 ```python def sum_of_squares(numbers): return sum(n**2 for n in numbers) ``` -So in addition to the “can I copy-paste my way from a loop to a comprehension” check, there’s another, fuzzier, check to consider: could your code be enhanced by a generator expression combined with an iterable-accepting function or class? +所以,除了要考虑检查“我是否可以从一个循环复制-粘贴到推导式”之外,我们还需要考虑:我们是否可以通过结合生成器表达式与接受迭代器的函数或者类来增强我们的代码? -Any function or class that **accepts an iterable as an argument** **might** be a good candidate for **combining with a generator expression**. +那些可以**接受迭代器作为参数**的函数或者类,**可能是与生成器表达式组合的**优秀组件。 -## Use list comprehensions thoughtfully +## 深思熟虑后使用列表推导式 -List comprehensions can make your code more readable (if you don’t believe me, see the examples in my [Comprehensible Comprehensions](https://youtu.be/5_cJIcgM7rw "Comprehensible Comprehensions") talk), but they can definitely be abused. +列表推导式可以使你的代码更可读(如果你不相信我,可以看我的演讲[可理解的推导式](https://youtu.be/5_cJIcgM7rw "Comprehensible Comprehensions")中的例子),但是它确实被滥用。 -List comprehensions are a special-purpose tool for solving a specific problem. The `list` and `dict` constructors are **even more special-purpose tools** for solving even more specific problems. +列表推导式是被用来解决特定问题的专用工具。`list` 和 `dict` 的构造函数是被用来解决更具体问题的更专用的工具。 -Loops are **a more general purpose tool** for times when you have a problem that doesn’t fit within the realm of comprehensions or another special-purpose looping tool. +循环是**更通用的工具**,适用于当你遇到的问题不适合推导式或其它专用循环工具领域的场景。 -Functions like `any`, `all`, and `sum`, and classes like `Counter` and `chain` are iterable-accepting tools that **pair very nicely with comprehensions** and sometimes **replace the need for comprehensions entirely**. +像 `any`、`all` 和 `sum` 这样的函数,以及像 `Counter` 和 `chain` 这样的类都是接受迭代器的工具,它们**与推导式**非常匹配,有时**完全取代了推导式**。 -Remember that comprehensions are for a single purpose: **creating a new iterable from an old iterable**, while tweaking values slightly along the way and/or for filtering out values that don’t match a certain condition. Comprehensions are a lovely tool, but **they’re not your only tool**. Don’t forget the `list` and `dict` constructors and always consider `for` loops when your comprehensions get out of hand. +请记住,推导式只有一个目的:**从旧的迭代器中创建一个新的迭代器**,同时在此过程中稍微调整值和/或过滤不匹配条件的值。推导式是一个可爱的工具,但是**它们不是你唯一的工具**。当你的推导式不能胜任时,不要忘记 `list` 和 `dict` 构造函数,以及 `for` 循环。 > 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 --- > [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[区块链](https://github.com/xitu/gold-miner#区块链)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计)、[人工智能](https://github.com/xitu/gold-miner#人工智能)等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。 + From ee24c01549f8129e3fd685ce2ce81f5f89d22de9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E6=9C=88=E6=BA=90?= <592302815@qq.com> Date: Fri, 12 Jul 2019 11:14:32 +0800 Subject: [PATCH 24/68] =?UTF-8?q?=E8=AE=BE=E8=AE=A1=E4=BB=BB=E4=BD=95?= =?UTF-8?q?=E5=9B=BE=E8=A1=A8=E7=9A=84=E5=85=AD=E9=A1=B9=E5=8E=9F=E5=88=99?= =?UTF-8?q?=20(#6083)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * translation_1 translation_1 * Proofreading_1 Proofreading_1 * 校对修改完成 校对修改完成 * 添加校对者 添加校对者 * Update redefining-data-visualization-at-google.md --- ...redefining-data-visualization-at-google.md | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/TODO1/redefining-data-visualization-at-google.md b/TODO1/redefining-data-visualization-at-google.md index e5640661b4e..f94095eaac8 100644 --- a/TODO1/redefining-data-visualization-at-google.md +++ b/TODO1/redefining-data-visualization-at-google.md @@ -2,88 +2,88 @@ > * 原文作者:[Manuel Lima](https://medium.com/@mslima) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/redefining-data-visualization-at-google.md](https://github.com/xitu/gold-miner/blob/master/TODO1/redefining-data-visualization-at-google.md) -> * 译者: -> * 校对者: +> * 译者:[MarchYuanx](https://github.com/MarchYuanx) +> * 校对者:[mymmon](https://github.com/mymmon), [shinichi4849](https://github.com/shinichi4849) -# Six Principles for Designing Any Chart +# 设计图表的六项原则 -> An introduction to Google’s new data visualization guidelines +> Google 新数据可视化指南的简介 ![](https://cdn-images-1.medium.com/max/2748/1*mXIcH44FAZKCRjX5g_lYmw.png) --- -In August 2017, a group of passionate designers, researchers, and engineers at Google came together to create a comprehensive set of data visualization guidelines — covering everything from color, shape, typography, iconography, interaction, and motion. The success of this collaboration sparked the formation of Google’s first fully dedicated Data Visualization team, which kicked off in May 2018. +2017 年 8 月,Google 的一群热情的设计师、研究人员和工程师聚集在一起,创建了一套全面的数据可视化指南 —— 涵盖了所有内容,包括颜色、形状、排版、图标、交互和动作。这个合作的成功促成了谷歌第一个完全专注于数据可视化团队的组建,该团队于 2018 年 5 月成立。 -Over the last year, we’ve continued to work on understanding the needs, requirements, and desires shaping how people visualize and interact with information. Now, we want to share our insights with creators everywhere. We’ve launched detailed public guidelines for [creating your own data visualizations](https://goo.gle/2ITQoTY), and distilled our top principles and considerations. Below, six strategies for designing any chart. +在过去的一年里,我们一直致力于理解需求和要求,来具象化人们如何进行信息的可视化和交互。现在,我们想与世界各地的开发者分享我们的见解。我们针对[创建您自己的数据可视化项目](https://goo.gle/2ITQoTY)推出了详细的公共指南,并提炼出了我们的首要原则和注意事项。以下是用于设计图表的六种策略。 --- -## Be honest +## 保持坦诚 -#### Data accuracy and integrity come first. Don’t distort or confuse the information for embellishment or partiality. Emphasize clarity and transparency. +#### 数据的准确性和完整性是首要的。不要歪曲或混淆信息以进行掩饰或偏袒。重视数据的清晰度和透明度。 ![](https://cdn-images-1.medium.com/max/2760/1*ydrVMlmFanX1LsuN6CzTqA.png) -Provide users with the contextual elements they need to understand a given visualization. Maximize the integrity of the graphic by using clear labels, accurate axes and baselines, and supporting tooltips and legends. Motion can help reinforce relationships, but must not distort the data. Be transparent about the employed dataset, where it came from, and how it was collected and treated. +为用户提供用于理解给定的可视化图表所需的上下文元素。通过使用清晰的标签、准确的轴线和基线,支持工具提示和图例组件,最大限度地提高图形的完整性。图表的运动有助于加强关联度,但不能歪曲数据。把所使用数据集的来源、收集和处理方式等信息一目了然地呈现 --- -## Lend a helping hand +## 伸出援手 -#### Provide context and help users navigate the data. Build affordances that prioritize data exploration and comparison. +#### 提供上下文并协助用户进行数据的导航。建立优先考虑数据探索和比较的可供性。 ![](https://cdn-images-1.medium.com/max/2760/1*60a7CCF8W4EytCv7idmllw.png) -Design with users’ existing mental models — which may be shaped by widely used tools — in mind. Create a warm onboarding experience that makes it easy to learn how to read the chart and its information. Select visual and interactive affordances that support discoverability of core features, such as selecting, zooming, panning, and filtering. Motion and interaction should support analytical reasoning and user comprehension by revealing context, insights, associations, and causality. Leverage empty states as moments of revelation. +设计时要考虑到用户现有的思维模式 —— 可能是受广泛使用的工具影响而定型的。创造温馨的入手体验,将使用户更容易去学习如何阅读图表以及包含的信息。选择能使核心功更易于被发现的视觉和交互的可供性功能。例如选择,缩放,平移和过滤。运动和交互应通过揭示背景、见解、关联和因果关系来支持分析推理和用户理解。利用空状态作为启示的时刻。 --- -## Delight users +## 取悦用户 -#### Always exceed expectations. Consider performance, polish, surprise, and innovation. Embrace dynamic, fast, and clever experiences. +#### 总是超越预期。考虑性能、优化、惊喜和创新。选择动态、快速、巧妙的体验。 ![](https://cdn-images-1.medium.com/max/2760/1*IpHoJvLE_87IDvRG8dQ3MQ.png) -Create great visualization experiences, then improve upon them in unexpected ways. When appropriate, employ signature features and small moments of delight that guide users to what they need. Speed is as rewarding as graphical excellence. Consider motion and timing in the choreography of state transitions to aid perception of a fast and responsive system. +创造出色的可视化体验,然后以意想不到的方式优化它们。在适当的时候,通过使用签名功能和一些小小的轻松时刻来引导用户找到他们所需要的。速度同出色的图形效果一样有价值。考虑状态转换编排中的运动和时间,有助于快速响应系统的感知。 --- -## Give clarity of focus +## 明确重点 -#### Reduce cognitive load and focus on what matters. Every action, color, and visual element should support data insights and understanding. +#### 减少感知的负担,专注于重要的事情。每个动作,颜色和视觉元素都应该服务于洞悉、理解数据。 ![](https://cdn-images-1.medium.com/max/2760/1*VwVvqEaH-Y3Z_5Ryt481gw.png) -Focus on the user’s task and all else should follow. Direct users to the essential information as quickly as possible. Maximize the data-ink ratio and avoid extraneous graphic elements. Apply color in meaningful ways to contribute to graph comprehension: label, group, highlight, or measure. Use motion sparingly–limit to subtle transitions and cues that help users understand hierarchy, data orientation, and relationships. +专注于用户的任务,其他一切都应该遵循。尽可能更快地引导用户了解基本信息。最大化数据笔墨的比例,避免出现无关图形元素。把颜色赋予意义,以加强图形理解:标签、分组、高亮或量度。请谨慎使用动作 —— 限制在使用细微的过渡和提示,帮助用户理解层次结构、数据方向和关系。 --- -## Embrace scale +## 接受扩展 -#### Allow the system to extend and adapt to any context. Respect different user needs on data depth, complexity, and modality. +#### 允许系统扩展以适应任意语境。尊重不同用户对数据深度、复杂度和形态的需求。 ![](https://cdn-images-1.medium.com/max/2760/1*DF5pg4i7OlWo9fAfbi-liQ.png) -Every chart should aim to be as accessible as possible. Consider how chart elements (color palettes, filter configuration, axes, panels, interactive mechanisms) might scale to accommodate a variety of users’ needs, screen sizes, and data types (from a single data point to large multivariate datasets). Think about a spectrum of possibilities rather than an immutable configuration. Apply interactive approaches to minimize complexity, such as providing details gradually (progressive disclosure), letting users change perspective, and linking different views to enable deeper insights. +每张图表都应该尽可能的便于访问。考虑图表元素(调色板、过滤器配置、轴、面板、交互机制)如何调整以适应用户的各种需求,屏幕大小和数据类型(从单个数据点到大型多变量数据集)。考虑一系列的可能性而不是不变的配置。使用交互式方法来最小化复杂性,例如逐步提供细节(渐进式披露),让用户更改视角,以及链接不同视图以实现更深入的洞察。 --- -## Provide structure +## 提供结构 -#### Use visual attributes to convey hierarchy, provide structure, and improve consistency. Experiences should be intuitive and easy to use. +#### 使用可视属性来传达信息层次,提供结构并提高一致性。体验应该是直观且易于使用的 ![](https://cdn-images-1.medium.com/max/2760/1*XJqqL_vhSWVNRpjbi_zn1g.png) -Consistency drives familiarity. Develop uniformity in graphical treatments (shape, color, iconography, typography) and interaction patterns (selection, filtering, hover states, expansion). Motion should feel controlled, giving the user a sense of stability and continuity while remaining responsive. Consider entrance and exit motion to help the user understand the visual hierarchy of elements, orientation of axes, and the data displayed. Maintain strong contextual cues, so no matter where the user navigates in the chart, they know how to get back. +一致性促进熟悉。在图形处理(形状、颜色、图像、排版)和交互模式(选择、过滤、悬停状态、扩展)中实现一致性。图表的运动应该是受控的,在保持响应的同时为用户提供稳定性和连续性。考虑进入和退出的运动,以帮助用户了解元素的视觉层次,轴的方向和数据的显示。保持足够的上下文提示,因此无论用户在图表中浏览到哪里,他们都知道如何返回。 -**For more insights and strategies, read our full [Data Visualization guidelines](https://goo.gle/2ITQoTY).** +**关于更多的见解和方案,请阅读我们完整的[数据可视化指南](https://goo.gle/2ITQoTY)。** --- -#### Acknowledgments +#### 致谢 -This work could not have been done without the talent and dedication of countless individuals at Google. Thank you: Shuo Yang, Kent Eisenhuth, Sharona Oshana, Katherine Meizner, Hael Fisher, Ross Popoff-Walker, Ian Johnson, Joe Nagle, Ryan Vernon, Nick Bearman, Luca Paulina, Gerard Rocha, JT DiMartile, Lorena Zalles, Tom Gebauer, Hilal Koyuncu, Bethany Fong, Ann Chou, Barbara Eldredge, and Anja Laubscher. +如果没有 Google 里无数人的才能和奉献精神,这项工作就无法完成。感谢:Shuo Yang, Kent Eisenhuth, Sharona Oshana, Katherine Meizner, Hael Fisher, Ross Popoff-Walker, Ian Johnson, Joe Nagle, Ryan Vernon, Nick Bearman, Luca Paulina, Gerard Rocha, JT DiMartile, Lorena Zalles, Tom Gebauer, Hilal Koyuncu, Bethany Fong, Ann Chou, Barbara Eldredge, and Anja Laubscher. > 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 From 2c0958a0e3d763533911f7fcdeb435f2fba19d9c Mon Sep 17 00:00:00 2001 From: sun <776766759@qq.com> Date: Fri, 12 Jul 2019 11:35:15 +0800 Subject: [PATCH 25/68] Create creating-a-multi-level-hierarchical-flyout-navigation-menu-using-only-html-and-css.md (#6117) --- ...navigation-menu-using-only-html-and-css.md | 337 ++++++++++++++++++ 1 file changed, 337 insertions(+) create mode 100644 TODO1/creating-a-multi-level-hierarchical-flyout-navigation-menu-using-only-html-and-css.md diff --git a/TODO1/creating-a-multi-level-hierarchical-flyout-navigation-menu-using-only-html-and-css.md b/TODO1/creating-a-multi-level-hierarchical-flyout-navigation-menu-using-only-html-and-css.md new file mode 100644 index 00000000000..ac466bf2381 --- /dev/null +++ b/TODO1/creating-a-multi-level-hierarchical-flyout-navigation-menu-using-only-html-and-css.md @@ -0,0 +1,337 @@ +> * 原文地址:[Creating a multi-level hierarchical flyout navigation menu using only HTML and CSS](https://www.ghosh.dev/posts/creating-a-multi-level-hierarchical-flyout-navigation-menu-using-only-html-and-css/) +> * 原文作者:[Abhishek Ghosh](https://www.ghosh.dev/) +> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) +> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/creating-a-multi-level-hierarchical-flyout-navigation-menu-using-only-html-and-css.md](https://github.com/xitu/gold-miner/blob/master/TODO1/creating-a-multi-level-hierarchical-flyout-navigation-menu-using-only-html-and-css.md) +> * 译者: +> * 校对者: + +# Creating a multi-level hierarchical flyout navigation menu using only HTML and CSS + +![](https://www.ghosh.dev/static/media/css-nav-menu-1.jpg) + +Today I am going to give you a quick tutorial on how to create a hierarchical navigation flyout menu that can go nested deep down across multiple levels. + +As an inspiration, we’ll start off with a concrete practical use-case of an example menu bar for a desktop application. I’ll pick a subset of the Chrome browser’s menu bar to illustrate this. + +We’ll begin with a simple quite look-and-feel, something that goes back to the classic Windows™ theme. Here’s a short video on how that would look like: + +[css-nav-menu-3.mp4](https://www.ghosh.dev/static/media/css-nav-menu-3.mp4) + +Towards the end, we’ll make it a bit fancier by adding some more styling to give it a MacOS™ like feel. + +### The Basics + +Let’s start off by understanding what our menu items would typically constitute of. They should have the following properties: + +* **Label**: (**required**) which is basically the name of the menu item that is displayed +* **Target**: (**optional**) a hyperlink that takes the user to a page as a response to clicking on the menu item. We’ll stick to just links right now. Adding more dynamic in-page features would require JavaScript which we’ll stay away from at the moment. It’s something you can always go and easily add later. +* **Shortcut**: (**optional**) in our case, displays a keyboard shortcut that could be used for this menu item. For example, “File > New” would be “Cmd + N” (⌘N) on Mac. +* **Children**: (**optional**) which refers to the sub-menu for this menu item. Think of our menus and sub-menus in the form of a **recursive structure**. Visually, a menu item having a sub-menu should also have an arrow icon on it (▶) to indicate that it can expand when hovered. +* **Disabled**: (optiona), a state indicating if the menu item can be interacted with. +* A conceptual **Type** parameter? (**optional**) that could emulate different types of menu items with this. Say, some entries in the list of menus should act as just a **separator** line. + +Note that we could go ahead and add more complex behaviours to our menus. For example, a certain menu could be a **Toggle** item, and so, would need to have some form of a tick mark (✔) or checkbox associated with it to indicate its on/off state. + +We’ll use **CSS classes** on our HTML markup to indicate such properties and write some clever styling to impart all the corresponding behaviours. + +### Structuring the HTML + +Based on the above, this is how our basic menu HTML should look like: + +1. A list of menus is defined by an HTML `ul` element, with individual items being the obvious `li`. +2. The **label** and **shortcut** will be placed as `span` elements with their corresponding CSS classes (`label` or `shortcut`) inside an anchor (`a`) tag inside the `li`, so that clicking on it causes the navigation action, as well as be able to provide some UI feedback such as highlighting the menu item on **hover**. +3. When a menu item contains a list of **sub-menu** (children), we’ll put that sub-menu in another `ul` element inside the current menu `li` element (parent) and so on. To describe that this particular menu item contains a sub-menu and also be able to add some specific styling to make it functional (as well as visual elements like the ▶ indicator), we’ll add the `has-children` CSS class to this parent `li`. +4. For items like the **separator**, we’ll add a corresponding CSS class called `separator` to the `li` item denoting it. +5. A menu item can be **disabled**, in which case we’ll add the corresponding `disabled` CSS class. It’s job is to make this item non-responsive to pointer events like hover or clicks. +6. We’ll wrap everything off inside a container HTML `nav` element (it’s good to be [semantic](https://en.wikipedia.org/wiki/Semantic_HTML)) and add the `flyout-nav` class to it for some basic namespacing of the CSS styles that we’ll add. + +```html + + +``` + +### Adding behaviours in CSS + +I lied. We’ll use [SCSS](https://sass-lang.com/) instead. + +Jokes aside, here comes the interesting part! + +The menu (except the first-level “horizontal bar”), should be **hidden** by default. + +Anything below the first level should only be displayed when the corresponding menu item is hovered upon using the mouse pointer. As you may have already guessed, we’ll heavily rely on the CSS [`hover` pseudo-class](https://developer.mozilla.org/en-US/docs/Web/CSS/:hover) for this. + +##### Arranging menu and sub-menu elements + +Perhaps the trickiest bit in this whole puzzle is to understand how we make the sub-menu position and align itself with respect to their parent menu item correctly. This is where some knowledge of CSS [positioning](https://developer.mozilla.org/en-US/docs/Web/CSS/position) comes in. Let’s look at that. + +There was a reason why we chose to put the sub-menu `ul` element inside a “parent” `li` element. Of course, it helps us to logically appropriately put together the markup for our hierarchical content, but it also serves another purpose of allowing us to easily write some CSS to position a child element **relative** to the position of a parent element. Then we take this concept all the way to the root `ul` and `li` elements. + +For doing this, we’ll use a combination of `absolute` positioning and `top`, `left` CSS properties that will help us to position a child element relative to its **closest non-static positioned ancestor** defining the [containing block](https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block). By non-static, we mean that the CSS position property for an element is not `static` (which happens by default in the HTML document flow), but instead is one of `relative`, `absolute`, `fixed` or `sticky`. To make sure of that, we’ll assign the position `relative` to the `li` elements with their child `ul` elements positioned `absolute`. + +```scss +.flyout-nav { + // list of menu items at any level + ul { + margin: 0; + padding: 0; + position: absolute; + display: none; + list-style-type: none; + } + + // a menu item + li { + position: relative; + display: block; + + // show the next level drop-down on + // the right at the same height + &:hover { + & > ul { + display: block; + top: 0; + left: 100%; + } + } + } +``` + +The effect of this is shown in the image below, highlighted in the red box for illustration. Some additional CSS for visual styling has been done in the image to make it look all nice, but the core behaviour is defined by what we have above. This keeps working great to N-levels deep (within limits of practicality). + +![Sub-menu positioning](https://www.ghosh.dev/static/media/css-nav-menu-4.jpg) + +There’s one exception to this though, which is the first-level list of menu items (File, Edit, View… in our example), whose children menu items need to be positioned **below** instead of right. To handle that, we add some style overrides to our previous CSS. + +```scss +.flyout-nav { + // ... other stuff + + // overrides for first-level behaviour (horizontal bar) + & > ul { + display: flex; + flex-flow: row nowrap; + justify-content: flex-start; + align-items: stretch; + + // first-level drop-down should appear + // below at the same left position + & > li:hover > ul { + top: 100%; + left: 0; + } + } +} +``` + +Note that using a flex-box here was not imperative, rather just something I did out of choice. You could achieve similar behaviour using other approaches such as a combination of `display: block` and `display: inline-block` on the `ul` and `li` items as well. + +##### [](#UI-polishing)UI polishing + +Once we’re done handling the basics of positioning the menu items, we’ll go on about writing some additional styles such as fonts, sizes, colours, backgrounds, shadow and such for making the UI feel all nice and better. + +For consistency and reuse, let’s also assume that we have such values defined and shared using a bunch of SCSS variables. Something like… + +```scss +// variables +$page-bg: #607d8b; +$base-font-size: 16px; // becomes 1rem +$menu-silver: #eee; +$menu-border: #dedede; +$menu-focused: #1e88e5; +$menu-separator: #ccc; +$menu-text-color: #333; +$menu-shortcut-color: #999; +$menu-focused-text-color: #fff; +$menu-text-color-disabled: #999; +$menu-border-width: 1px; +$menu-shadow: 2px 2px 3px -3px $menu-text-color; +$menu-content-padding: 0.5rem 1rem 0.5rem 1.75rem; +$menu-border-radius: 0.5rem; +$menu-top-padding: 0.25rem; +``` + +There are some pieces that we’re left adding the appropriate styles and behaviours for. We’ll go over them quickly now. + +##### Anchors, Labels and Shortcuts - the actual visual elements + +```scss +.flyout-nav { + // ... other stuff + + li { + // ... other stuff + + // the menu items - text, shortcut info and hover effect (blue bg) + a { + text-decoration: none; + color: $menu-text-color; + position: relative; + display: table; + width: 100%; + + .label, + .shortcut { + display: table-cell; + padding: $menu-content-padding; + } + + .shortcut { + text-align: right; + color: $menu-shortcut-color; + } + + label { + cursor: pointer; + } + + // for menu items that are toggles + input[type='checkbox'] { + display: none; + } + + input[type='checkbox']:checked + .label { + &::before { + content: '✔️'; + position: absolute; + top: 0; + left: 0.25rem; + padding: 0.25rem; + } + } + + &:hover { + background: $menu-focused; + .label, + .shortcut { + color: $menu-focused-text-color; + } + } + } + } +} +``` + +Most of this code is pretty self-explanatory. However, did you notice anything interesting? The bit about `input[type='checkbox']`? + +##### [](#Toggle-Items)Toggle Items + +For toggles, we use a hidden HTML `checkbox` element to maintain state (on or off) and style the `label` with [`::before` pseudo-element](https://developer.mozilla.org/en-US/docs/Web/CSS/::before) accordingly. We are able to do that using a simple CSS [adjacent sibling selector](https://developer.mozilla.org/en-US/docs/Web/CSS/Adjacent_sibling_combinator). + +The corresponding HTML markup for that menu item would look something like this: + +```html +
  • + + + + ⇧⌘B + +
  • +``` + +##### Separators + +```scss +.flyout-nav { + // ... other stuff + + li { + // ... other stuff + + // the separator item + &.separator { + margin-bottom: $menu-top-padding; + border-bottom: $menu-border-width solid $menu-separator; + padding-bottom: $menu-top-padding; + } + } +} +``` + +##### Disabled + +```scss +.flyout-nav { + // ... other stuff + + li { + // ... other stuff + + // don't let disabled options respond to hover + // or click and color them different + &.disabled { + .label, + .shortcut { + color: $menu-text-color-disabled; + } + pointer-events: none; + } + } +} +``` + +CSS [pointer-events](https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events) does the actual trick here. Setting it to `none` makes it invisible as a target for any pointer events. + +### Putting it all together… + +Now that we’ve gained some understanding of the building blocks, let’s put it all together. Here’s a Codepen link to our multi-level hierarchical flyout navigation menu in action! + +Demo:[CSS-only multi-level hierarchical navigation flyout menu](https://codepen.io/abhishekcghosh/pen/WqjOaX) + +##### Fancier theming + +If you are not a fan of the retro Windows look, here’s another version of the same code with some minor tweaks to the CSS to make it look and feel more like MacOS. + +Demo:[CSS-only multi-level hierarchical navigation flyout menu (MacOS lookalike)](https://codepen.io/abhishekcghosh/pen/qzmEWd) + +### What doesn’t work? + +There are a few things we haven’t handled. For starters, + +* If you’re nitpicky about it, while most of the behaviour works great, a limitation of the deliberate CSS-only approach is that unlike the real-world Windows and MacOS application menus, our menu hides immediately as soon as the pointer goes outside. For more comfortable usage, typically what we’d want to do is wait for a click before hiding (can be always achieved with a bit of JS). +* What if the list of items in a menu is super long? Imagine a bookmarks list as an example. At some point, it might need to be capped into a scrollable view, say at some percentage of the viewport height. At the end of the day, it’s really a choice of the user experience you’re building, but something I wanted to put out there as well. + +Hope this was useful. Cheers! + +> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 + +--- + +> [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[区块链](https://github.com/xitu/gold-miner#区块链)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计)、[人工智能](https://github.com/xitu/gold-miner#人工智能)等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。 From 5fa8ae4f56f17760f091220b85976e5b27b28dd8 Mon Sep 17 00:00:00 2001 From: sun <776766759@qq.com> Date: Fri, 12 Jul 2019 11:40:36 +0800 Subject: [PATCH 26/68] Create css-architecture-for-multiple-websites.md (#6119) --- .../css-architecture-for-multiple-websites.md | 207 ++++++++++++++++++ 1 file changed, 207 insertions(+) create mode 100644 TODO1/css-architecture-for-multiple-websites.md diff --git a/TODO1/css-architecture-for-multiple-websites.md b/TODO1/css-architecture-for-multiple-websites.md new file mode 100644 index 00000000000..d184a3a5930 --- /dev/null +++ b/TODO1/css-architecture-for-multiple-websites.md @@ -0,0 +1,207 @@ +> * 原文地址:[CSS Architecture for Multiple Websites](https://medium.com/@elad/css-architecture-for-multiple-websites-ad696c9d334) +> * 原文作者:[Elad Shechter](https://medium.com/@elad) +> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) +> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/css-architecture-for-multiple-websites.md](https://github.com/xitu/gold-miner/blob/master/TODO1/css-architecture-for-multiple-websites.md) +> * 译者: +> * 校对者: + +# CSS Architecture for Multiple Websites + +> CSS Architecture — Part 3 + +Complex CSS architecture is not something you learn in any formal institution. + +My fourth job in the web industry was as a CSS/HTML expert, in one of the leading media news companies in my country, and my primary mission was to write reusable and scalable CSS for multiple websites. + +![](https://cdn-images-1.medium.com/max/2000/1*WreGgi4zIgKz_cb5vRjGTA.png) + +In this post, I will share with you the knowledge and experience I gained in this field of constructing a multiple website architecture. + +Side note: nowadays, a proper project uses a CSS preprocessor. In this post, I will be using the SASS preprocessor. + +This post is the third in a series of articles I’m writing about CSS architecture. To understand this post better, I recommend that you read at least the second post in this series, “[CSS Architecture — Folders & Files Structure](https://medium.com/@elad/css-architecture-folders-files-structure-f92b40c78d0b)”. + +## Layers World + +Starting a large-scale project requires thinking globally and defining what things the sites have in common. It begins with little things, like normalize, mixins, shared icons, and the partials layer (elements, components, sequences, entities, pages, and more). + +For the projects (sites) to work correctly, you have to decide which styles appear in enough of the sites to warrant defining them in the base layer, and which styles aren’t, and therefore should be defined in the specific project in which they appear. It’s a practice you will achieve by trial and error. It often happens that you move styles from layer to layer when your perspective changes, until you get them balanced in a way that suits you. + +After understanding this principle, you can begin making the underlying global layer. This global layer will be the starting point for all the projects (sites). + +Here’s an example of a diagram that demonstrates the requirements of the company I worked for at that time. + +![Layer Structures](https://cdn-images-1.medium.com/max/2000/1*zYZV-QHyYrA_1XwxibQw2A.png) + +The base layer should be thin, only containing CSS resets, base SASS mixins, shared icons, general font (if needed), utility classes, and maybe shared grids if it suits all projects. For the `_partials.scss` layer (elements, components, etc.), you will mostly use the `_elements.scss` layer which has partials like common-popup, common forms, and common titles. You only add styles that are shared by all, or most, of the lower layers. ([More about Folders & Files Structure is detailed in my previous post](https://medium.com/@elad/css-architecture-folders-files-structure-f92b40c78d0b)) + +#### How to Structure the Layers + +In our architecture, each layer has at least three files; 2 private files (local and config; I call them private because they don’t get compiled into a CSS file) and one public file (the primary layer file). The layer’s configuration file, `**_config.scss**` usually contains variables. The `**_local.scss**` file includes the content styles and acts as a kind of controller or a package manager for the layer. **The third** file **calls** those first two files **(layer-name.scss**). + +**layer-name.scss file:** + +``` +@import "config"; +@import "local"; +``` + +Another principle that we should set for ourselves is to try and divide everything into the smallest parts (small files) possible. This principle will become very handy when you get to refactoring. + +In every layer, **compile only the layer-name.scss** file. You should do this even in layers representing a ‘Virtual Project’ like the ‘Base Layer Framework’ in the above diagram. + +For the private files, which aren’t compiled to a separate CSS file, we use an underscore (“`_`”) as a prefix in all file names. The underscore symbols a file which can’t stand on its own. + +**Notice:** When importing private files, you can write their names without the underscore prefix. + +**Example of layer structure:** + +![The **_local.scss file includes all *.scss files in the local folder**, which, in turn, calls **all *.scss files in the private folders**. The **_config.scss file calls all files in the config folders**.](https://cdn-images-1.medium.com/max/2000/1*0hwUrfXGWkZR-aTVfoojyA.png) + +**How it looks in the folders** + +``` +sass/ + | + |- base-layer/ + |- config/ + |- local/ + |- _config.scss + |- _local.scss + |- base-layer.css (compiled layer style) + |- base-layer.scss +``` + +## Inheritance + +Imagine we want to create a project from the base layer. We will have to build a parallel folder with the project’s name. In the following example, we’ll call it **inherited-project**. + +**Note**: Locate all layers and projects in the SASS root folder. + +The project has at least one `**_config.scss**` file, one `**_local.scss**` file, and the layer’s central sass file named, in our example, `**inherited-project.scss**`. + +All layers/projects sit in the root folder of SASS. + +``` +sass/ + | + |- base-layer + | |- config/ + | |- local/ + | |- _config.scss + | |- _local.scss + | |- base-layer.css (compiled layer style) + | |- base-layer.scss + | + |- inherited-project + |- config/ + |- local/ + |- _config.scss + |- _local.scss + |- inherited-project.css (compiled layer style) + |- inherited-project.scss +``` + +The **inherited-project**’s config file imports the **base-layer**’s config file. This way, we can add new variables, or override existing ones from the layer above (**base-layer**). + +Here’s an **example** of the- **inherited-project/_config.scss**: + +``` +/*load base-layer configuration */ +@import "../base-layer/config.scss"; + +/** local Config layer (add or override variables if needed)**/ +@import "config/directions.scss"; +``` + +The same goes for the **inherited-project/_local.scss** content files of the layer. + +``` +/* import base-layer local components */ +@import "../base-layer/local.scss"; + +/* local font */ +@import "local/font-almoni.scss"; + +/* local components*/ +@import "local/elements.scss"; +@import "local/components.scss"; +``` + +Inheriting from the base-project folder is the right way to build a new layer that has its unique style, based on the inheritance from the base layer. + +This layer will create one CSS file, called `**inherited-project.css**`. + +#### Override variable in inner layer + +It’s straightforward to override variables using the ‘layers’ method. + +Let’s say we have a variable named `**$base-color**` in the base layer and its value is blue (`**$base-color: blue**`;). Overriding this variable requires updating its value in the **local** `_config.scss`. Now all the components that use this variable — whether inherited from the **base layer** or defined in the **local layer** — will be updated with the value of the overridden color. + +## Global Story + +Some partials aren’t used in all layers, and therefore if you define them in the base layer, the other projects will import unnecessary code. To solve this problem, I implemented another idea of **global partials** concept. + +This concept is that partials which are used only in some layers will be placed in another new root folder (`_partials`) outside of any layer. Then, any layer that needs these partials can import them from the `_partials` global folder. + +**This diagram** illustrates **an example** of separated partials: + +![](https://cdn-images-1.medium.com/max/2000/1*F43F_4fEqXCCTLNz07nrqg.png) + +Every layer can call a single partial or multiple ones from the global `**_partials**` folder, as needed. + +**Example of a global _partials folder:** + +``` +sass/ + | + |- _partials/ + |- base-layer/ + |- inherited-project/ +``` + +**local.scss file view of — import global partial:** + +``` +/* import base-layer local components */ +@import "../base-layer/local.scss"; + +/*local components*/ +@import "local/partials.scss"; + +/* add global partial */ +@import "../_partials/last-connection"; +``` + +**Few extra guidelines** + +* **Be well organized**. Always organize your projects and maintain the best structure in a way that fits your needs. +* **Don’t repeat yourself**. You can import other layers’ components by simply `@import`ing them directly. For example, let’s say some components are defined in the ‘sports’ project, and they’re relevant to the ‘news’ site of another project. We can `@import` those components to the ‘news’ site. (site = layer = project) +* **Utilize IDE shortcuts**. Use a code editor that enables easy refactoring without causing errors or bugs. +* **Make sure you don’t break anything while you work**. Compile all root SASS files while working and continuously refactor the code to see that nothing breaks. + +## To Summarize + +In this post, I showed my CSS Architecture approach for a multiple-websites architecture, based on the knowledge and experience I’ve gained over the years. + +This post is the third in a **new series of articles on CSS Architecture** I have written, and I will share with you every few weeks. + +If it is interesting you , you are welcome to follow me on [**twitter**](https://twitter.com/eladsc) or [**medium**](https://medium.com/@elad). + +## My CSS Architecture Series: + +1. [Normalize CSS or CSS Reset?!](https://medium.com/@elad/normalize-css-or-css-reset-9d75175c5d1e) +2. [CSS Architecture — Folders & Files Structure](https://medium.com/@elad/css-architecture-folders-files-structure-f92b40c78d0b) +3. [CSS Architecture for Multiple Websites](https://medium.com/@elad/css-architecture-for-multiple-websites-ad696c9d334) + +## Final Words + +That’s all; +I hope you’ve enjoyed this article and learned from my experience. +If you like this post, I would appreciate applause and sharing :-) + +> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 + +--- + +> [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[区块链](https://github.com/xitu/gold-miner#区块链)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计)、[人工智能](https://github.com/xitu/gold-miner#人工智能)等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。 From 14f54b96295fe8f9233f4bc0e3f2b28d552df2fb Mon Sep 17 00:00:00 2001 From: sun <776766759@qq.com> Date: Fri, 12 Jul 2019 11:45:44 +0800 Subject: [PATCH 27/68] Update css-architecture-for-multiple-websites.md --- TODO1/css-architecture-for-multiple-websites.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/TODO1/css-architecture-for-multiple-websites.md b/TODO1/css-architecture-for-multiple-websites.md index d184a3a5930..3b07200a949 100644 --- a/TODO1/css-architecture-for-multiple-websites.md +++ b/TODO1/css-architecture-for-multiple-websites.md @@ -37,7 +37,7 @@ The base layer should be thin, only containing CSS resets, base SASS mixins, sha #### How to Structure the Layers -In our architecture, each layer has at least three files; 2 private files (local and config; I call them private because they don’t get compiled into a CSS file) and one public file (the primary layer file). The layer’s configuration file, `**_config.scss**` usually contains variables. The `**_local.scss**` file includes the content styles and acts as a kind of controller or a package manager for the layer. **The third** file **calls** those first two files **(layer-name.scss**). +In our architecture, each layer has at least three files; 2 private files (local and config; I call them private because they don’t get compiled into a CSS file) and one public file (the primary layer file). The layer’s configuration file, **`_config.scss`** usually contains variables. The **`_local.scss`** file includes the content styles and acts as a kind of controller or a package manager for the layer. **The third** file **calls** those first two files **(layer-name.scss**). **layer-name.scss file:** @@ -78,7 +78,7 @@ Imagine we want to create a project from the base layer. We will have to build a **Note**: Locate all layers and projects in the SASS root folder. -The project has at least one `**_config.scss**` file, one `**_local.scss**` file, and the layer’s central sass file named, in our example, `**inherited-project.scss**`. +The project has at least one **`_config.scss`** file, one **`_local.scss`** file, and the layer’s central sass file named, in our example, **`inherited-project.scss`**. All layers/projects sit in the root folder of SASS. @@ -130,13 +130,13 @@ The same goes for the **inherited-project/_local.scss** content files of the lay Inheriting from the base-project folder is the right way to build a new layer that has its unique style, based on the inheritance from the base layer. -This layer will create one CSS file, called `**inherited-project.css**`. +This layer will create one CSS file, called **`inherited-project.css`**. #### Override variable in inner layer It’s straightforward to override variables using the ‘layers’ method. -Let’s say we have a variable named `**$base-color**` in the base layer and its value is blue (`**$base-color: blue**`;). Overriding this variable requires updating its value in the **local** `_config.scss`. Now all the components that use this variable — whether inherited from the **base layer** or defined in the **local layer** — will be updated with the value of the overridden color. +Let’s say we have a variable named **`$base-color`** in the base layer and its value is blue (**`$base-color: blue`**;). Overriding this variable requires updating its value in the **local** `_config.scss`. Now all the components that use this variable — whether inherited from the **base layer** or defined in the **local layer** — will be updated with the value of the overridden color. ## Global Story @@ -148,7 +148,7 @@ This concept is that partials which are used only in some layers will be placed ![](https://cdn-images-1.medium.com/max/2000/1*F43F_4fEqXCCTLNz07nrqg.png) -Every layer can call a single partial or multiple ones from the global `**_partials**` folder, as needed. +Every layer can call a single partial or multiple ones from the global **`_partials`** folder, as needed. **Example of a global _partials folder:** From 3ef25b9b9078e1d785fe199c54b1316614c0bc9e Mon Sep 17 00:00:00 2001 From: sun <776766759@qq.com> Date: Fri, 12 Jul 2019 12:55:13 +0800 Subject: [PATCH 28/68] Create how-pagespeed-works.md (#6121) --- TODO1/how-pagespeed-works.md | 164 +++++++++++++++++++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 TODO1/how-pagespeed-works.md diff --git a/TODO1/how-pagespeed-works.md b/TODO1/how-pagespeed-works.md new file mode 100644 index 00000000000..965179b540c --- /dev/null +++ b/TODO1/how-pagespeed-works.md @@ -0,0 +1,164 @@ +> * 原文地址:[How Google Pagespeed works: Improve Your Score and Search Engine Ranking](https://calibreapp.com/blog/how-pagespeed-works/) +> * 原文作者:[Ben Schwarz](https://calibreapp.com/blog/author/ben-schwarz) +> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) +> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/how-pagespeed-works.md](https://github.com/xitu/gold-miner/blob/master/TODO1/how-pagespeed-works.md) +> * 译者: +> * 校对者: + +# How Google Pagespeed works: Improve Your Score and Search Engine Ranking + +![](https://calibreapp.com/blog/uploads/how-google-pagespeed-works/1.png) + +In this article, we uncover how PageSpeed calculates it’s critical speed score. + +It’s no secret that speed has become a crucial factor in increasing revenue and lowering abandonment rates. Now that Google uses page speed as a ranking factor, many organisations have become laser-focused on performance. + +Last year **Google made two significant changes to their search indexing and ranking algorithms**: + +* In March, [indexing became based on the mobile version of a page](https://webmasters.googleblog.com/2018/03/rolling-out-mobile-first-indexing.html), rather than desktop. +* [In July, the SEO ranking algorithm](https://webmasters.googleblog.com/2018/01/using-page-speed-in-mobile-search.html) was updated to include page speed as a ranking factor for both mobile pages [and ads.](https://developers.google.com/web/updates/2018/07/search-ads-speed#the_mobile_speed_score_for_ads_landing_pages) + +From this, we’re able to state two truths: + +* **The speed of your site on mobile will affect your overall SEO ranking.** +* If your pages load slowly, it will reduce your ad quality score, and **ads will cost more.** + +Google wrote: + +> Faster sites don’t just improve user experience; recent data shows that improving site speed also reduces operating costs. Like us, our users place a lot of value in speed — that’s why we’ve decided to take site speed into account in our search rankings. + +To understand how these changes affect us from a performance perspective, we need to grasp the underlying technology. [PageSpeed 5.0](https://developers.google.com/speed/docs/insights/release_notes) is a complete overhaul of previous editions. It’s now being powered by Lighthouse and [CrUX](https://developers.google.com/web/updates/2017/12/crux) (Chrome User Experience Report). + +**This upgrade also brings a new scoring algorithm that makes it far more challenging to receive a high PageSpeed score.** + +### What changed in PageSpeed 5.0? + +Before 5.0, PageSpeed ran a series of heuristics against a given page. If the page has large, uncompressed images, PageSpeed would suggest image compression. No Cache-Headers missing? Add them. + +These heuristics were coupled with a set of **guidelines** that would **likely** result in better performance if followed, but were merely superficial and didn’t actually analyse the load and render experience that real visitors face. + +In PageSpeed 5.0, pages are loaded in a real Chrome browser that is controlled by Lighthouse. Lighthouse records metrics from the browser, applies a scoring model to them and presents an overall performance score. Guidelines for improvement are suggested based on how specific metrics score. + +Like PageSpeed, Lighthouse also has a performance score. In PageSpeed 5.0, the performance score is taken from Lighthouse directly. **PageSpeed’s speed score is now the same as Lighthouse’s Performance score.** + +![Calibre scores 97 on Google’s Pagespeed](https://calibreapp.com/blog/uploads/how-google-pagespeed-works/calibre-pagespeed.png) + +Now that we know where the PageSpeed score comes from, let’s dive into how it’s calculated, and how we can make meaningful improvements. + +### What is Google Lighthouse? + +[Lighthouse](https://calibreapp.com/blog/lighthouse-reasons/) is an open source project run by a dedicated team from Google Chrome. Over the past couple of years, it has become **the** go-to free performance analysis tool. + +Lighthouse uses Chrome’s Remote Debugging Protocol to read network request information, measure JavaScript performance, observe accessibility standards and measure user-focused timing metrics like [First Contentful Paint](https://calibreapp.com/docs/metrics/paint-based-metrics), [Time to Interactive](https://calibreapp.com/docs/metrics/time-to-interactive) or Speed Index. + +If you’re interested in a high-level overview of Lighthouse architecture, [read this guide](https://github.com/GoogleChrome/lighthouse/blob/master/docs/architecture.md) from the official repository. + +### How Lighthouse calculates the Performance Score + +During performance tests, Lighthouse records many metrics focused on what a user sees and experiences. + +There are 6 metrics used to create the overall performance score. They are: + +* Time to Interactive (TTI) +* Speed Index +* First Contentful Paint (FCP) +* First CPU Idle +* First Meaningful Paint (FMP) +* Estimated Input Latency + +Lighthouse will apply a 0 – 100 scoring model to each of these metrics. This process works by obtaining mobile 75th and 95th percentiles from [HTTP Archive](https://httparchive.org/), then applying a `log normal` function. + +[Following the algorithm and reference data used to calculate Time to Interactive](https://www.desmos.com/calculator/2t1ugwykrl), we can see that if a page managed to become “interactive” in 2.1 seconds, the Time to Interactive metric score would be 92/100. + +![](https://calibreapp.com/blog/uploads/how-google-pagespeed-works/scoring-curve.png) + +Once each metric is scored, it’s assigned a weighting which is used as a modifier in calculating the overall performance score. The weightings are as follows: + +| Metric | Weighting | +| ------------------------- | --------- | +| Time to Interactive (TTI) | 5 | +| Speed Index | 4 | +| First Contentful Paint | 3 | +| First CPU Idle | 2 | +| First Meaningful Paint | 1 | +| Estimated Input Latency | 0 | + +These weightings refer to the impact of each metric in regards to mobile user experience. + +In the future, this may also be enhanced by the inclusion of user-observed data from the Chrome User Experience Report dataset. + +You may be wondering how the weighting of each metric affects the overall performance score. The Lighthouse team [have created a useful Google Spreadsheet calculator](https://docs.google.com/spreadsheets/d/1Cxzhy5ecqJCucdf1M0iOzM8mIxNc7mmx107o5nj38Eo/edit#gid=0) explaining this process: + +![Picture of a spreadsheet that can be used to calculate performance scores](https://calibreapp.com/blog/uploads/how-google-pagespeed-works/weightings.png) + +Using the example above, if we change (time to) interactive from 5 seconds to 17 seconds (the global average mobile TTI), our score drops to 56% (aka 56 out of 100). + +Whereas, if we change First Contentful Paint to 17 seconds, we’d score 62%. + +**Time to Interactive (TTI) is the most impactful metric to your performance score.** + +Therefore, to receive a high PageSpeed score, you will **need** a speedy TTI measurement. + +### Moving the needle on TTI + +At a high level, there are two significant factors that hugely influence TTI: + +* The amount of JavaScript delivered to the page +* The run time of JavaScript tasks on the main thread + +Our [Time to Interactive](https://calibreapp.com/blog/time-to-interactive/) guide explains how TTI works in great detail, but if you’re looking for some quick no-research wins, we’d suggest: + +**Reducing the amount of JavaScript** + +Where possible, remove unused JavaScript code or focus on only delivering a script that will be run by the current page. That might mean removing old polyfills or replacing third-party libraries with smaller, more modern alternatives. + +It’s important to remember that [the cost of JavaScript](https://medium.com/reloading/javascript-start-up-performance-69200f43b201) is not only the time it takes to download it. The browser needs to decompress, parse, compile and eventually execute it, which takes non-trivial time, especially in mobile devices. + +Effective measures for reducing the amount of script from your pages: + +* Review and remove polyfills that are no longer required for your audience. +* Understand the cost of each third-party JavaScript library. Use [webpack-bundle-analyser](https://www.npmjs.com/package/webpack-bundle-analyzer) or [source-map-explorer](https://www.npmjs.com/package/source-map-explorer) to visualise the how large each library is. +* Modern JavaScript tooling (like Webpack) can break-up large JavaScript applications into a series of small bundles that are automatically loaded as a user navigates. This approach is known as [code splitting](https://webpack.js.org/guides/code-splitting/) and is **extremely effective in improving TTI.** +* [Service workers will cache the bytecode result of a parsed + compiled script](https://v8.dev/blog/code-caching-for-devs). If you’re able to make use of this, visitors will pay a one-time performance cost for parse and compilation, after that it’ll be mitigated by cache. + +### Monitoring Time to Interactive + +To successfully uncover significant differences in user experience, we suggest using a performance monitoring system (like [Calibre](https://calibreapp.com/)!) that allows for testing a minimum of two devices; a fast desktop and a low-mid range mobile phone. + +That way, you’ll have the data for both the best and worst case of what your customers experience. It’s time to come to terms that your customers aren’t using the same powerful hardware as you. + +### In-depth manual profiling + +To get the best results in profiling JavaScript performance, test pages using intentionally slow mobile devices. If you have an old phone in a desk drawer, this is a great second-life for it. + +An excellent substitute for using a real device is to use Chrome DevTools hardware emulation mode. We’ve written an extensive [performance profiling guide](https://calibreapp.com/blog/react-performance-profiling-optimization/) to help you get started with runtime performance. + +## What about the other metrics? + +Speed Index, First Contentful Paint and First Meaningful Paint are all browser-paint based metrics. They’re influenced by similar factors and can often be improved at the same time. + +It’s objectively easier to improve these metrics as they are calculated by how quickly a page renders. Following the Lighthouse Performance audit rules closely will result in these metrics improving. + +If you aren’t already preloading your fonts or optimising for critical requests, that is an excellent place to start a performance journey. Our article, [The Critical Request](https://calibreapp.com/blog/critical-request/), explains in great detail how the browser fetches and renders critical resources used to render your pages. + +## Tracking your progress and making meaningful improvements + +Google’s newly updated search console, Lighthouse and PageSpeed Insights are a great way to get initial visibility into the performance of your pages but fall short for teams who need to continuously track and improve the performance of their pages. + +[Continuous performance monitoring](https://calibreapp.com/features) is essential to ensuring speed improvements last, and teams get instantly notified when regressions happen. Manual testing introduces unexpected variability in results and makes testing from different regions as well as on various devices nearly impossible without a dedicated lab environment. + +Speed has become a crucial factor for SEO rankings, especially now that nearly 50% of Web traffic comes from mobile devices. + +To avoid losing positioning, ensure you’re using an up-to-date performance suite to track key pages (pssst, we built [Calibre](https://calibreapp.com/blog/release-notes-lighthouse-4/) to be your performance companion. It has Lighthouse built-in. Hundreds of teams from around the globe are using it every day). + +### Related Articles + +* [About Time to Interactive](https://calibreapp.com/blog/time-to-interactive/) +* [How to optimise the performance of a JavaScript application](https://calibreapp.com/blog/react-performance-profiling-optimization/) +* [Lighthouse Performance score Calculator](https://docs.google.com/spreadsheets/d/1Cxzhy5ecqJCucdf1M0iOzM8mIxNc7mmx107o5nj38Eo/edit#gid=283330180) + +> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 + +--- + +> [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[区块链](https://github.com/xitu/gold-miner#区块链)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计)、[人工智能](https://github.com/xitu/gold-miner#人工智能)等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。 From 6154f0d297920eb1e6d1bb134d69f29d86d2c192 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E6=9C=88=E6=BA=90?= <592302815@qq.com> Date: Sat, 13 Jul 2019 11:37:04 +0800 Subject: [PATCH 29/68] =?UTF-8?q?CSS=20=E6=80=9D=E7=BB=B4=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=20(#6124)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * CSS 思维模式 CSS 思维模式 * adjust adjust * colon add colon & change some word * 校对修改完成 校对修改完成 * 校对修改_2 校对修改_2 --- TODO1/the-css-mindset.md | 88 ++++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/TODO1/the-css-mindset.md b/TODO1/the-css-mindset.md index 58b71fd7723..3c7360c4281 100644 --- a/TODO1/the-css-mindset.md +++ b/TODO1/the-css-mindset.md @@ -2,48 +2,48 @@ > * 原文作者:[Max Böck](https://mxb.dev/about/) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/the-css-mindset.md](https://github.com/xitu/gold-miner/blob/master/TODO1/the-css-mindset.md) -> * 译者: -> * 校对者: +> * 译者:[MarchYuanx](https://github.com/MarchYuanx) +> * 校对者:[solerji](https://github.com/solerji), [Chorer](https://github.com/Chorer) -# The CSS Mindset +# CSS 思维模式 -Ah yes, CSS. Hardly a week passes without it being the topic of a heated online discussion. It’s too hard. It’s too simple. It’s unpredictable. It’s outdated. Peter Griffin struggles with blinds dot gif. +啊,是的,CSS。它几乎每个星期都是网络上热议的话题。它太难了。它太简单了。它无法预测。它过时了。此处应该有一张 Peter Griffin 折腾百叶窗的恶搞图。 -I don’t know why CSS sparks so many different emotions in developers, but I have a hunch as to why it can sometimes seem illogical or frustrating: You need a certain **mindset** to write good CSS. +我不知道为什么 CSS 会在开发者中激发出如此多的不同情绪,但我有一种直觉,为什么有时候它看起来不合逻辑或令人沮丧:你需要一种特定的**思维模式**才能写出好的 CSS。 -Now, you probably need a mindset for coding in general, but the declarative nature of CSS makes it particularly difficult to grasp, especially if you think about it in terms of a “traditional” programming language. +现在,您可能需要一个编码的思维模式,但 CSS 的声明性使其特别难以掌握,尤其是当您用“传统”编程语言的思维来思考它的时候。 -Other programming languages often work in controlled environments, like servers. They expect certain conditions to be true at all times, and can therefore be understood as concrete instructions as to how a program should execute. +其他编程语言通常在受控的环境中工作,例如服务器。它们希望某些条件始终为真,从而作为程序应该如何执行的具体指令。 -CSS on the other hand works in a place that can never be fully controlled, so it has to be flexible by default. It’s less about “programming the appearance” and more about translating a design into a set of rules that communicate the intent behind it. Leave enough room, and the browser will do the heavy lifting for you. +另一方面,CSS 在一个永远无法完全可控的地方工作,所以默认情况下它必须是灵活的。它不仅仅用于“编写外观”,还用于将设计转换为一组传达其背后意图的规则。留出足够的空间,浏览器将为您完成繁重的工作。 -For most people who write CSS professionally, the mindset just comes naturally after a while. Many developers have that “aha!” moment when things finally start to click. It’s not just about knowing all the technical details, it’s more about a general sense of the ideas behind the language. +对于大多数专业编写 CSS 的人来说,他的思维模式在一段时间后自然而然地形成了。在事情终于有成效的时候,许多开发者都有那样一个“啊哈!”的时刻。这不仅仅是了解所有技术细节,更多的是关于语言背后的思想的一种感觉。 -I tried to list some of these here. +我试着在这里列出一些思维模式。 -### Everthing is a Rectangle +### 全都是矩形 -This seems obvious, given that the box model is probably one of the first things people learn about CSS. But picturing each DOM element as a box is crucial to understanding why things layout the way they do. Is it inline or block level? Is it a flex item? How will it grow/shrink/wrap in different contexts? +这看起来很明显,因为盒子模型可能是人们学习 CSS 的时候接触到的第一个东西。但是将每个 DOM 元素描绘成一个盒子对于理解事物布局方式至关重要。它是内联的还是块级的?它是弹性的吗?它将如何在不同的环境中拉伸/收缩/包裹? -Open your devtools and hover over elements to see the boxes they’re drawing, or use a utility style like `outline: 2px dotted hotpink` to visualize its hidden boundaries. +打开你的开发者工具并将鼠标悬停在元素上,观察它们绘制的盒子。或使用像 `outline: 2px dotted hotpink` 这样的显式样式来显示其隐藏的边框。 -### The Cascade is your Friend +### 级联是你的朋友 -The Cascade - a scary concept, I know. Say “Cascade” three times in front of a mirror and somewhere, some unrelated styling will break. +级联 —— 一个可怕的概念,我懂。在镜子前说三次“级联”,在某个地方,一些不相关的样式会失效。 -While there are legitimate reasons to avoid the cascade, it doesn’t mean that it’s all bad. In fact, when used correctly, it can make your life a lot easier. +虽然有合理的理由避免级联,但这并不意味着它的一切都是不好的。事实上,如果使用得当,它可以让您的生活更轻松。 -The important part is to know which styles belong on the global scope and which are better restricted to a component. It also helps to know the defaults that are passed down, to avoid declaring unnecessary rules. +重要的是要知道哪些样式属于全局作用域,哪些样式更适合于组件。它还有助于了解传递的默认值,避免声明不必要的样式规则。 -### As much as necessary, as little as possible +### 尽可能必要,尽可能少 -Aim to write the minimal amount of rules necessary to achieve a design. Fewer properties mean less inheritance, less restriction and less trouble with overrides down the line. Think about what your selector should essentially do, then try to express just that. There’s no point in declaring `width: 100%` on an element that’s already block-level. There’s no need to set `position: relative` if you don’t need a new stacking context. +旨在编写实现设计所需的最少量的代码。较少的属性意味着更少的继承、更少的限制和更少的覆盖带来的麻烦。想想你的选择器应该做什么,然后尝试就那样表达它。在已经是块级别的元素上声明 `width: 100%` 是没有意义的。如果您不需要新的堆叠上下文,则无需设置 `position: relative`。 -Avoid unnecessary styles, and you avoid unintended consequences. +避免不必要的样式,你就避免了意外后果。 -### Shorthands have long effects +### 简写有很大的影响 -Some CSS features can be written in “shorthand” notation. This makes it possible to declare a bunch of related properties together. While this is handy, be aware that using the shorthand will also declare the default value for each property you don’t explicitly set. Writing `background: white;` will effectively result in all these properties being set: +一些 CSS 属性可以用“简写”方式书写,这使得一起声明一组相关属性成为可能。虽然这很方便,请注意,使用简写还将为未显式设置的每个属性声明默认值。写上 `background: white;` 将有效地导致所有这些属性被设置: ```css background-color: white; @@ -56,54 +56,54 @@ background-clip: border-box; background-attachment: scroll; ``` -It’s better to be explicit. If you want to change the background color, use `background-color`. +样式最好是明确的。 如果要更改背景颜色,请使用 `background-color`。 -### Always Be Flexible +### 永远要灵活 -CSS deals with a large amount of unknown variables: screen size, dynamic content, device capabilities - the list goes on. If your styles are too narrow or restrictive, chances are one of these variables will trip you up. That’s why a key aspect in writing good CSS is to embrace its flexibility. +CSS 处理大量未知的变量:屏幕大小、动态内容、设备功能 —— 这个列表还在继续。如果你的样式过于狭隘或受限,那么这些因素中的某一个很可能会让你栽跟头。这就是为什么写好 CSS 的一个关键方面就是接受它的灵活性 -Your goal is to write a set of instructions that is comprehensive enough to describe what you want to achieve, yet flexible enough to let the browser figure out the **how** by itself. That’s why its usually best to avoid **“magic numbers”**. +你的目标是编写一套足够全面的指令来描述你想要实现的页面,但足够灵活,让浏览器自己理解清楚**怎么做**。这就是为什么通常最好避免 **“神奇数字”**。 -Magic numbers are random hard values. Something like: +神奇数字是随机的固定值。比如: ```css .thing { width: 218px; /* why? */} ``` -Whenever you find yourself tapping the arrow key in your devtools, adjusting a pixel value to make something fit - that’s probably a magic number. These are rarely the solution to a CSS problem, because they restrict styles to a very specific usecase. If the constraints change, that number will be off. +每当你自己在开发工具中点击箭头键并调整一个像素值使之适合的时候 —— 都可能会有一个神奇数字。它们很少能解决 CSS 问题,因为它们将样式限制在特定的使用案例中。如果约束发生变化,那么该数字将会失效。 -Instead, think about what you actually want to achieve in that situation. Alignment? An aspect ratio? Distributing equal amounts of space? All of these have flexible solutions. In most cases, it’s better to define a rule for the intent, rather than hard-code the computed solution to it. +相反,想想在那种情况下你真正想要实现什么。对齐?宽高比?分配等量的空间?所有这些都有灵活的解决方案。在大多数情况下,最好为目的定义一个规则,而不是采用硬编码的计算方案。 -### Context is Key +### 语境是关键 -For many layout concepts it’s imperative to understand the relationship between elements and their container. Most components are sets of parent and child nodes. Styles applied to the parent can affect the descendants, which might make them ignore other rules. Flexbox, Grid and `position:absolute` are common sources of such errors. +对于许多布局概念,必须了解元素与其容器之间的关系。大多数组件是父节点和子节点的集合。应用于父级的样式会影响子孙级,这可能会使它们忽略其他规则。弹性盒子,栅格布局和 `position: absolute` 是此类错误的常见来源。 -When in doubt about a particular element behaving different than you’d want it to, look at the context it’s in. Chances are something in its ancestry is affecting it. +当疑惑某个特定元素的表现与您期望的不同时,请查看它所在的上下文。可能是它祖先级的某些因素影响了它。 -### Content will change +### 内容会改变 -Always be aware that what you see is just one UI state in a bigger spectrum. Instead of styling the thing on your screen, try to build a “blueprint” of the component. Then make sure that whatever you throw at it won’t break your styling. +始终要注意,您所看到的只是在大范围中的一种 UI 状态。不要在屏幕上设置样式,而是尝试构建组件的“蓝图”。然后确保不论你把它放在什么场景,都不会使你的样式失效。 -Strings may be longer than intended or contain special characters, images might be missing or have weird dimensions. Displays may be very narrow or extremely wide. Those are all states you need to anticipate. +字符串可能比预期长或包含特殊字符,图像可能缺失或具有奇怪的尺寸。显示的样子可能非常窄或非常宽。这些都是您需要预测的状态。 -The number one mistake made by designers and developers alike is assuming that things will always look like they do in the static mockup. I can assure you, they will not. +设计师和开发者犯的第一个错误就是假设事情总是像它们在静态模型中那样。我可以向你保证,它们不会。 -### Find Patterns and re-use them +### 发现模式并复用它们 -When you set out to turn a design mockup into code, it’s often helpful to take inventory of the different patterns included first. Analyse each screen and take note of any concept that occurs more than one. It might be something small like a typographic style, or large like a certain layout pattern. +当您打算将设计模型实现为代码时,首先盘点出所包含的不同模式通常很有帮助。分析每个屏幕的场景,注意任何出现一次以上的概念。它可能是一些小的东西,比如排版样式,或者大的东西,比如某种布局模式。 -What can be abstracted? What’s unique? Thinking of pieces in a design as standalone things makes them easier to reason about, and helps to draw the boundaries between components. +什么可以抽象?什么是特有的?将设计中的各个部分视为独立的东西使它们更易于理解,并有助于划分组件之间的界限。 -### Use consistent Names +### 使用一致的命名 -A surprisingly large part of programming in general is coming up with good names for stuff. In CSS, it helps to stick to a convention. Naming schemes like [BEM](http://getbem.com) or [SMACSS](http://smacss.com/) can be very helpful; but even if you don’t use them, stick to a certain vocabulary. +总的来说,相当多的一部分程序有不错的命名。在 CSS 中,它有助于遵守约定。像 [BEM](http://getbem.com) 或 [SMACSS](http://smacss.com/) 这样的命名方案非常有用;但即使你不使用它们,也要坚持使用一致的词汇。 --- -👉 **DISCLAIMER** -All these things were important for me to understand, but your personal experience as to what matters most might be different. Did you have another “aha” moment that made you understand CSS better? Let me know! +👉 **免责声明** +所有这些对我来说都是很重要的,但是基于你的个人经历,什么最重要可能是不同的。你有没有你的“啊哈”时刻让你更好地理解 CSS?告诉我! -## Further Reading +## 延伸阅读 * [How to learn CSS](https://www.smashingmagazine.com/2019/01/how-to-learn-css/) by Rachel Andrews * [The Secret Weapon to learning CSS](https://css-tricks.com/the-secret-weapon-to-learning-css/) by Robin Rendle From e6ed3c3d844750dc01caf42c9f5852d43d156bbe Mon Sep 17 00:00:00 2001 From: Yuqi Date: Sat, 13 Jul 2019 11:41:31 +0800 Subject: [PATCH 30/68] =?UTF-8?q?=E4=BD=BF=E7=94=A8=20Gomobile=20=E5=92=8C?= =?UTF-8?q?=20Gopherjs=20=E7=9A=84=E5=8A=A8=E6=80=81=E4=BA=8C=E7=BB=B4?= =?UTF-8?q?=E7=A0=81=E6=95=B0=E6=8D=AE=E4=BC=A0=E8=BE=93=20(#6096)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update animated-qr-data-transfer-with-gomobile-and-gopherjs.md * Update animated-qr-data-transfer-with-gomobile-and-gopherjs.md --- ...ata-transfer-with-gomobile-and-gopherjs.md | 192 +++++++++--------- 1 file changed, 96 insertions(+), 96 deletions(-) diff --git a/TODO1/animated-qr-data-transfer-with-gomobile-and-gopherjs.md b/TODO1/animated-qr-data-transfer-with-gomobile-and-gopherjs.md index 44d227d9906..93d4707bb9b 100644 --- a/TODO1/animated-qr-data-transfer-with-gomobile-and-gopherjs.md +++ b/TODO1/animated-qr-data-transfer-with-gomobile-and-gopherjs.md @@ -2,89 +2,89 @@ > * 原文作者:[Divan](https://divan.dev) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/animated-qr-data-transfer-with-gomobile-and-gopherjs.md](https://github.com/xitu/gold-miner/blob/master/TODO1/animated-qr-data-transfer-with-gomobile-and-gopherjs.md) -> * 译者: -> * 校对者: +> * 译者:[EmilyQiRabbit](https://github.com/EmilyQiRabbit) +> * 校对者:[suhanyujie](https://github.com/suhanyujie) -# Animated QR data transfer with Gomobile and Gopherjs +# 使用 Gomobile 和 Gopherjs 的动态二维码数据传输 -TL;DR: a weekend project for transferring data via animated QR codes, written in Go and using fountain erasure codes. The Go code is reused for mobile apps using Gomobile, and in a web application for automating testing QR codes parameters, built with GopherJS and Vecty framework. +太长了不想看,直接给出全文结论:这是在周末开展,想要通过动态二维码传递数据的项目,项目采用 Go 语言编程并使用了喷泉抹除码。移动端应用使用了 Gomobile,可以复用 Go 代码,而对于网页应用,为了自动化测试二维码参数,项目使用 GopherJS 和 Vecty 框架构建。 -![Transfer file via QR code between two phones](https://divan.dev/images/txqr_send.gif#center) +![在两个手机之间通过二维码传输文件](https://divan.dev/images/txqr_send.gif#center) -I’ll share my experience building it, some code and benchmark results of using animated QR as a data transfer method. +我将会分享构建这个项目的经验,以及使用动态二维码作为数据传输方式的代码和基准测试的结果。 -![Testing results](https://divan.dev/images/results_3d.png) +![测试结果](https://divan.dev/images/results_3d.png) -## The problem +## 疑难问题 -One day I was trying to find a viable solution for the following scenario: +曾经有一天,我试着想找到如下这个场景下可行的解决方案: -Imagine you’re in the crowded place and suddenly your messenger stop working because your authoritarian government blocked it. Maybe they just banned IP addresses of bootnodes for the messenger, or restricted access to some hosts via state-controlled DNS providers, or cut off yet another VPN or proxy service – it doesn’t actually relevant here. The question is – how to re-enable connectivity if at least one device has a working connection and willing to share connectivity information with others. +假设你正在一个人流拥挤的地方,忽然间收发消息的应用停止工作了,因为独裁政府阻止了通讯。也许他们只是封禁了收发消息使用节点的 IP 地址,或者通过国有 DNS 提供器限制了一些主机的访问权,又或者是切断了其他的 VPN 和代理服务 —— 在这里何种方式其实并不重要。问题是 —— 如果至少有一台设备还能成功连网,如何能恢复其他人的网络连接,并能和他人分享连网信息呢。 -Now, that’s rather a long introduction, but one important concept has to be understood here – [percolation threshold](https://en.wikipedia.org/wiki/Percolation_threshold). The simplified idea is that if you connect nodes in lattice or graph (or people in the crowd) with probability **p**, then at some critical probability **p**0 large clusters and long-range connectivity will appear. In even simpler words, if everyone in a crowd will share information with **n** people around them, you can guarantee with mathematical precision that everyone will get this information. +这部分如果展开来说,篇幅就会很长了,但是这其中最重要的、必须明白的概念就是 —— [渗流阈值](https://en.wikipedia.org/wiki/Percolation_threshold)。简单的说就是,如果你用概率 p 将格子或图(或者人群中的人)中的节点连接起来,那么在某个临界概率 **p**0 处,将会出现较大的集群和远距离链接。用更简单的话说,如果人群中的每个人都和他们周围 **n** 个人共享信息,那么你可以运用数学的准确性保证,每个人都会得到这些信息。 -In the case of bootnodes IPs, for example, that means, that connectivity of the app will be restored for everyone. +而在例如启动节点 IP 的情境下,这就意味着,每个人的应用的连接都将会恢复。 -Ok, now the question – how do you quickly send an arbitrary piece of small data from one app to another, when you’re in the adversarial environment, with the partly blocked internet? Bluetooth comes to mind first, but that requires a long and tedious process of discovering the device, figuring out its device name, and in many cases, it just “doesn’t connect, don’t know why” type of problems. Next, NFC is a good idea – just tap one phone to another – but the major drawback is that there are still plenty of phones and tablets out there without or with limited NFC support. How about QR codes? +好了,现在回到我们的问题 —— 当你处于一个对抗性的环境中,并且被切断了部分网络,如何能快速的将任意的信息碎片从一个应用发送到另一个呢?最先想到的是使用蓝牙,但是这需要一个冗长枯燥的发现设备和识别设备名的过程,并且在很多时候,蓝牙就是会出现“不知为什么而无法连接”的问题。另外,NFC 也是个不错的主意 —— 可以直接将一个手机连接到另一个手机 —— 但是主要的缺点是,还有很多手机和平板设备并不支持 NFC 或者是对 NFC 的支持还有限。那么,二维码如何呢? -## QR Codes +## 二维码 -[QR codes](https://en.wikipedia.org/wiki/QR_code) are an extremely popular type of visual encoding, widely adopted across many industries. It allows for different error recovery levels, with almost 30% redundancy for the highest one. Version with the highest capacity (QR Version 40) allows you to encode up to 4296 alphanumeric or 2953 binary symbols. +[二维码](https://en.wikipedia.org/wiki/QR_code)是一种非常受欢迎的视觉编码,在多种行业都广泛应用。它支持多种不同的错误恢复级别,最高可有将近 30% 的冗余信息。容量最高的版本(版本 40)允许编码最多 4296 个字母或者 2953 个二进制符号。 -But here are two obvious problems: +但是有两个很明显的问题: -* 3-4KB might be just not enough -* The more data in QR code, the better quality and image resolution should be +* 3-4KB 的传输速度恐怕是不够的 +* 二维码中包含的数据越多,图像的质量和解析精度就要越高 -For my case I wanted to be able to transfer approximately ~15KB of data on the average consumer devices, so I naturally asked why not use animated QR codes with dynamic FPS and size changes? +在本例中,我想要能够在性能中等的用户设备之间传输大约 15KB 的数据,所以很自然的想到,为什么不用具有动态 FPS 和大小会变动的动态二维码呢? -A quick research of prior work showed a [few](https://github.com/leonjza/qrxfer) [similar](https://volumeintegration.com/jumping-the-gap-data-transmission-over-an-air-gap/) [projects](http://stephendnicholas.com/posts/quicker-video-qr-codes), mostly as hackathon projects or even as a [bachelor’s thesis](http://www.theseus.fi/bitstream/handle/10024/96359/Grasbeck_Max.pdf?sequence=1&isAllowed=y) and written in Java, Python or JavaScript. That means the code is not cross-platform and virtually non-reusable and has to be reimplemented from scratch. Luckily, due to the massive popularity of QR codes, there is no shortage of libraries to work with them, and QR decoding is even embedded in the camera software of major smartphones manufacturers. (That’s actually was one of the reasons of not exploring other ways to increase the capacity of QR code, like colored QR codes, where color encodes additional layers or even cooler stuff with luminance encoding like Apple uses for their [particle cloud in Apple Watch pairing process](https://www.youtube.com/watch?v=-WK4jiwlE5k)). +我快速调研了一些前人做的工作,发现有[一些](https://github.com/leonjza/qrxfer) [类似的](https://volumeintegration.com/jumping-the-gap-data-transmission-over-an-air-gap/) [项目](http://stephendnicholas.com/posts/quicker-video-qr-codes),大部分是作为黑客项目,或者甚至是作为[学位论文](http://www.theseus.fi/bitstream/handle/10024/96359/Grasbeck_Max.pdf?sequence=1&isAllowed=y),并且使用了 Java、Python 或者 JavaScript 作为编程语言。这就意味着,这些代码并不能跨平台,也不能真正的被复用,所以我的项目必须要从零开始实现。幸运的是,由于二维码非常流行,很多人都在使用,所以并不缺少于此相关的代码库,同时,二维码的解析甚至已经嵌入在大多数智能手机制造商的相机软件中。(这实际上也正是人们不去研发其他方法来增加二维码功能的原因,比如说彩色二维码,在彩色二维码中,颜色编码附加层,或者甚至是亮度编码这种更酷的东西,像苹果用于他们的[Apple Watch 配对过程中的粒子云](https://www.youtube.com/watch?v=-WK4jiwlE5k)那样)。 ## TXQR -So that’s how my weekend project started. Name TxQR stands for Transfer (Tx) via QR codes. +我的周末项目就是这样开始的。TxQR 这个单词的意思就是通过二维码传输(Tx)。 -The basic design is following. One client chooses the data to be sent, generates animated QR code and shows it in the loop until the reader receives all the frames. Encoding is designed in such a way, that it allows for any particular order of frames, as well as dynamic changes in FPS or encoded chunks size. That was made for the case where the reader is much slower, and it can display a message “please decrease FPS on sender”, and continue receiving the same file, even if the frame size has changed. +如下就是本项目主要的设计思路。客户端需要选择一个准备发送的数据,生成动态二维码然后循环展示它们,直到读取设备收到了所有帧。编码被设计成如下方式,它可以支持任意的帧的次序,也支持 FPS 或者编码数据块大小的动态变化。这是为了读取设备读取的很慢而设计的,并且它还可以展示信息“请降低发送设备的 FPS”,然后继续接收同一个文件,甚至连帧体积也会改变。 -The protocol is quite simple – each frame starts with a prefix “NUM/TOTAL|”, (where NUM and TOTAL are integer values for current and total frames respectively) and the rest is the file content. For binary content, original data is encoded using Base64, so only alphanumeric data is actually encoded in QR. Then frames are shown with a given FPS in the infinite loop. +TXQR 协议非常简单 —— 每一帧都以 “NUM/TOTAL|” 开始,(NUM 和 TOTAL 都是整数,分别表示当前正在收发的帧以及帧总数)其余的就是文件内容。为了生成二进制的内容,原始数据使用 Base64 编码,所以实际上只有字母和数字被编码到了二维码里。然后所有帧就以给定的 FPS 无限循环展示。 -![TXQR protocol](https://divan.dev/images/txqr_protocol.png) +![TXQR 协议](https://divan.dev/images/txqr_protocol.png) -It’s that simple, and [here](https://github.com/divan/txqr) is a Go implementation of the protocol, along with convenient wrappers for encoding and decoding QR codes. The cool part was to make a mobile app that can use this code. +它非常简单,[这里](https://github.com/divan/txqr)有一个本协议的 Go 语言的实现,并且为了编解码二维码,已经做了简便的封装。它最酷的部分是让移动端应用也可以使用这个代码。 -**UPD: txqr now uses much more efficient approach using [fountain codes](https://en.wikipedia.org/wiki/Fountain_code). See the [follow-up article](https://divan.dev/posts/fountaincodes/) with detailed description and results comparison.** +**更新:txqr 现在使用更加有效的方法即[喷泉码](https://en.wikipedia.org/wiki/Fountain_code)。[后续的文章](https://divan.dev/posts/fountaincodes/)里有详细讲解和测试结果比较,有兴趣可以查看。** ### Gomobile -And it was an extremely easy task thanks to [gomobile](https://github.com/golang/mobile). +多亏了有 [gomobile](https://github.com/golang/mobile),这个项目就变得非常简单了。 -You just write a standard Go code, then run `gomobile bind ...` and in a few seconds get prepared `.framework` or `.aar` file to be included in your iOS or Android project. In the project, you refer to it as to any regular library and get autocomplete and type information automatically. +文件中有你刚写好的标准 Go 语言代码,然后运行 `gomobile bind ...`,几秒钟以后就可以将 `.framework` 或者 `.aar` 文件加入到你的 iOS 或者 Android 项目中去了,你可以像其他常规库那样引用它,并且可以自动获取名称补全以及类型信息。 -I quickly built a simple iOS QR scanner in Swift (thanks to the [fantastic intro](https://medium.com/appcoda-tutorials/how-to-build-qr-code-scanner-app-in-swift-b5532406dd6b) on that by Simon Ng) and modified it to read animated QR codes, feed the decoded chunks into txqr decoder and display received the file in a preview window. +我迅速的用 Swift 搭建了一个简单的 iOS 二维码扫描器(多亏了 Simon Ng 的[精彩介绍](https://medium.com/appcoda-tutorials/how-to-build-qr-code-scanner-app-in-swift-b5532406dd6b)),然后将其调整为可以读取动态二维码,它将需要解码的数据块提供给 txqr 解码器,然后在一个预览窗口展示接收到的文件。 -Whenever I got stuck on “how to do X properly in Swift”, it usually was much easier to implement it in Go and then just call the method from lib. Don’t get me wrong, Swift is a nice language in many aspects, but it allows you to things in multiple ways plus has some breaking changes history, and you end up googling and stackoverflowing for an hour for simple things like “how to measure duration with millisecond precision”. After 40 minutes of wasted time, I just did Go function with `time.Since(start)` call and used it from within the Swift. +每当我被“如何在 Swift 中做某某事”这样的问题困扰的时候,使用 Go 语言来解决通常要简单的多,然后只需要像上文描述的那样,直接调用库中的方法即可。但是请大家不要误会,Swift 在很多方面的表现都很出色,是很优秀的编程语言,但是它会对一件事情提供很多的解决方案,再加上有很多变动巨大的历史版本,导致你总是需要花很长时间在 Google 或者 stackoverflow 上搜索一些像“如何计算毫秒精度的时间”这样简单的问题。在浪费了 40 分钟以后,我决定使用 Go 语言,只需要调用函数 `time.Since(start)`,然后将代码转换并在 Swift 里面直接使用。 -I also wrote a terminal tool that displays QR codes in the console for quickly testing the app. Combined, that worked suprisingly well - I was able to send the small image in approximately ten seconds, but once I started to test on bigger files and playing with different FPS, I realized that terminal QR implementation frame rate wasn’t enough to test higher rates and that trying it manually is a dead end. +我也写了一个命令行工具,它可以在控制台展示二维码,用于对应用进行快速测试。综合所有这些,这个方案得工作状态格外优秀 —— 我可以在大约十秒钟内发送体积较小的图片,但是我开始测试大一些的文件并且尝试不同的 FPS 的时候,我意识到终端应用的二维码的帧率不足以测试更高的传输速度,如果手动进行高帧率测试可能会让应用卡死。 -## TXQR Tester +## TXQR 测试 -![TXQR tester](https://divan.dev/images/txqr_tester.jpg) +![TXQR 测试](https://divan.dev/images/txqr_tester.jpg) -If I want to find the optimal combination of FPS, QR frame size and QR recovery level I have to run at least 1000 tests, manually changing parameters and writing down results to the spreadsheets, while holding the phone near the screen. No way. I obviously should be able to automate that! +如果我希望找到最优 FPS、二维码帧体积以及二维码恢复级别的组合,我需要进行至少 1000 次的测试,手动调整参数并在表单记录结果,并且还要一直举着手机对准屏幕。太麻烦了,没门儿。很明显,我应该将这个步骤自动化。 -That’s where the idea of txqr tester app came in. First, I decided to use [x/exp/shiny](x/exp/shiny) which is a UI framework for desktop apps in Go, but it seems to as experimental as it is abandoned. That’s a pity as I played with `shiny` a year ago and it was promising for low-level desktop apps. When I tried it today, it didn’t even compile. It seems like there is no incentive to develop desktop UI frameworks anymore – everything is on the web. +所以我需要一个 txqr 测试应用。首先,我决定使用 Go 语言实现的桌面应用 UI 框架 [x/exp/shiny](x/exp/shiny),但是它似乎还是个试验性的框架,所以我就放弃了它。真是很遗憾,因为一年前我尝试过使用 `shiny`,那时候它在简单的桌面应用上很有发展前途。但是我现在再尝试用它的时候,它甚至已经无法编译了。似乎是开发桌面 UI 框架没什么动力了 —— 因为现在大多数的应用都是在 web 端。 -But web programming, you know, is still in the very early stages – programming languages just started to be supported by browsers via WASM, but they’re just making their first baby steps. There is, of course, JavaScript, but friends don’t let friends write web apps in JavaScript, so I decided to use one recent discovery of mine - framework [Vecty](https://github.com/gopherjs/vecty), which allows you to write frontends in Go and automagically transpile it to JS via surprisingly mature project [GopherJS](https://github.com/gopherjs/gopherjs). +但是 web 编程依旧在发展的初期阶段 —— 浏览器才刚刚可以通过 WASM 而支持其他编程语言,但也仅仅是起步。当然,你可以用 JavaScript,但是作为朋友我还是不建议你使用 JavaScript 来写 web 应用,所以我决定使用我最近发现的一个项目 —— [Vecty](https://github.com/gopherjs/vecty) 框架,这样你就可以用 Go 语言写前端代码然后通过一个非常成熟的项目 [GopherJS](https://github.com/gopherjs/gopherjs) 自动编译成 JS。 -### Vecty and GopherJS +### Vecty 和 GopherJS ![Vecty](https://raw.githubusercontent.com/vecty/vecty-logo/master/horizontal_color.png) -Honestly, I never had more fun writing frontends. +老实说,我以前从没有这么愉快的写过前端代码。 -I’ll blog more about my recent experience with Vecty, including developing WebGL apps, but the point is – after writing quite a few projects in React, Angulars and Ember, doing it in a well-designed language is such a breath of fresh air! I can now write pretty decent web apps without writing any single line of Javascript, and, in most cases, “it just works”! +关于我近期使用 Vecty 的经历,我将会写更多的博客来介绍,包括开发 WebGL 应用等等,但是重要的是 —— 在使用 React、Angulars 和 Ember 写了几个项目后,能用一个设计精良的语言来实现这个项目实在是拨云见日般的感觉!我现在可以不用写一行 JavaScript 代码就完成一个不错的 web 应用,并且在大多数情况下,“它真的可以运行”! -Just to tease you, here is how you write a web app in Go these days: +开个玩笑啦,下面就是如今用 Go 写 web 应用的方法: ``` package main @@ -102,10 +102,10 @@ func main() { } ``` -The app is just a type – a struct that embeds `vecty.Core` type – and it has to implement `vecty.Component` interface. That’s it! Constructing DOM objects seems a bit verbose at start, but you fully appreciate how actually great it is only when start refactoring the code: +一个应用就是一个类型 —— 一个嵌入了 `vecty.Core` 类型的结构体 —— 并且需要实现接口 `vecty.Component`。这就行了!初始化 DOM 对象一开始看上去有些冗长,但是当你开始重构代码的时候,你就会清楚的意识到它实际上是如此厉害了: ``` -// App is a top-level app component. +// App 是一个顶层的应用组建 type App struct { vecty.Core @@ -115,7 +115,7 @@ type App struct { // it's just a struct } -// Render implements the vecty.Component interface. +// Render 实现了接口 vecty.Component func (a *App) Render() vecty.ComponentOrHTML { return elem.Body( a.header(), @@ -123,14 +123,14 @@ func (a *App) Render() vecty.ComponentOrHTML { vecty.Markup( vecty.Class("columns"), ), - // Left half + // 左半边 elem.Div( vecty.Markup( vecty.Class("column", "is-half"), ), - elem.Div(a.QR()), // QR display zone + elem.Div(a.QR()), // 二维码显示区域 ), - // Right half + // 右半边 elem.Div( vecty.Markup( vecty.Class("column", "is-half"), @@ -150,100 +150,100 @@ func (a *App) Render() vecty.ComponentOrHTML { } ``` -You’re probably looking at this code and thinking how verbose it is, and it definitely is, but it’s so pleasant to work with! No open/closing tags, super easy copy/paste operation (if you need to move some DOM nodes around), the structure is clear and makes sense, and it’s all strongly typed! You’ll appreciate its verbosity after writing your own components, I’m quite sure. +你也许在审视这段代码并且觉得它非常冗长,我承认确实是,但是写代码的过程却是很愉悦!不需要 html 的开始/结束标签,就是非常简单的复制粘贴操作(如果你想要移动一些 DOM 节点),代码结构非常分明,可读性也比较高,同时都是强类型的!我向你保证,当你开始写自己的组件的时候,你就会觉得它的冗长是非常有用的了。 -Vecty is said to be a React-like framework, but it’s not completely accurate. There are GopherjS bindings to React exists - [myitcv.io/react](https://github.com/myitcv/x/blob/master/react/_doc/README.md), but I don’t think we need to follow the same steps as React did. When you write frontend in Vecty, you realize how much simpler things are. You don’t need most of the hidden magic and new terms that most JavaScript frameworks invent – they’re all just accidental complexity. You still just need types and functions and methods and connect and call them properly, that’s it. +人们都认为 Vecty 是一个和 React 类似的项目,但是这种说法并不准确。确实有 GopherjS 与 React 的绑定 —— [myitcv.io/react](https://github.com/myitcv/x/blob/master/react/_doc/README.md),但是我不认为我们需要仿照和 React 相同的做法。当你使用 Vecty 写前端的时候,你会意识到事情其实非常简单。你并不需要大多数 JavaScript 框架创造出来的隐藏的高级用法和新特性 —— 这些只是个别的比较复杂内容。你只需要类型、函数和方法,将它们组合好,然后适时调用,就可以了。 -For the CSS I used surprisingly good CSS framework called [Bulma](https://bulma.io) \- it has some sane and meaningful naming, making resulting UI code really digestible. +关于 CSS,我使用了超好用的 CSS 框架 [Bulma](https://bulma.io) —— 它提供了一些逻辑清晰并且有意义的命名,让编译结果的 UI 代码非常易读。 -The real magic happens on the compilation stage, though. You just run `gopherjs build`, and in less than a second, you got your compiled JS ready to be included in your page or server by your server. When I first run it, I expected to see a bunch of errors, warnings, unreadable messages, but no - it’s blazingly fast, silent and only reports compilation errors as oneliners. In the browsers, by the way, if panic is thrown, you see stacktraces with references to Go files (not the transpiled JS) and line numbers in it. Isn’t it cool? +然而最神奇的部分是编译阶段。你只需要运行 `gopherjs build`,然后只需要不到一秒钟,你就得到了编译好的 JS 代码,可以作为页面引用或者服务器的服务了。当我第一次运行它的时候,我本来以为会有一堆的错误、警告或者不可读信息,但是并没有 —— 它速度非常块,默默完成了所有任务,仅在一行中显示了一些编译错误。顺便说一下,在浏览器端,如果有错误抛出,你可以看到链接到 Go 文件的栈追踪(而不是编译好的 JS 文件),并且还能看到代码所处的行数。是不是超酷的? -### Testing the TXQR encoder parameters +### 测试 TXQR 编码参数 -So, in a few hours, I got the web app that allows me to configure the range of testing parameters: +就这样,几个小时后,我完成了能让我配置测试参数范围的 web 应用: -* FPS (frame per second) -* QR frame size (how many bytes to put in each QR frame) -* QR Recovery level (Low, Medium, High and/or Highest) +* FPS(每秒帧数) +* 二维码帧体积(每个二维码帧中可以承载多少比特) +* 二维码恢复界别(低,中,高或者最高) -and initiate a testing sequence for the mobile app. +然后就可以初始化移动端应用的测试程序了。 -![TXQR tester app](https://divan.dev/images/txqr_app.png) +![TXQR 测试应用](https://divan.dev/images/txqr_app.png) -The mobile app, of course, should be automated as well – it needs to understand when the next testing round starts, handle timeouts (sometimes phone camera just can’t pick up all frames and never get the result), sends results to the app, etc. +当然,移动端应用也需要自动化 —— 应用需要能识别下一轮测试什么时候开始,并需要处理超时(有时候手机摄像头无法获取到所有帧,也就无法得到结果),还要将结果发送给应用,等等。 -There was a tricky part with the fact that web app can’t create listening sockets – it runs in the browsers, and apart of WebRTC (which I thought would be an overkill) for such a simple communication testing protocol, you only can be a client. +一个比较麻烦的部分是,web 应用无法创建监听 socket —— 它运行在浏览器中,对于这样一个简单的通信测试协议,除了使用 WebRTC 之外(我觉得并没有 必要),你只能作为客户端使用而不能创建。 -So the solution was quite simple – small Go app that will serve as an HTTP static server for web app (and automatically spin up the browser for you), will also include WebSocket proxy which will expect only two connections – from UI (our web app) and from mobile phone - and that will act like a transparent proxy, so both clients think they talk to each other directly. They have to be on the same WiFi network, of course. +解决方案其实很简单 —— 小型的 Go 应用可以作为 web 应用的 HTTP 静态服务(并可以自动提供浏览器功能),并且还可以包含预计只有两个连接的 WebSocket 代理 —— 来自于 UI(或者说 web 应用)的连接以及来自于移动端的连接 —— 这是一个透明的代理,从两个客户端的角度来看,可以认为它们在直接传递信息。当然,它们必须要在一个 WiFi 网络中。 -There still a need to somehow communicate WebSocket address to the mobile app, and guess what – we can use QR code for that :) So the flow is following: +另外还需要想办法将 WebSocket 地址传递给移动端应用,你猜怎么着 —— 你可以使用二维码完成这个任务 :) 综上,工作流如下: -* mobile app looks for QR code with “start” marker -* from the marker, it extracts “ws://” URL and connects to the given server -* UI app immediately recognizes this and starts generating next QR testing round -* it shows new QR code with “readyToStart?” marker -* the mobile app reads this QR code and sends the WebSocket message with a confirmation +* 移动端应用寻找二维码中的 “start” 标记 +* 从标记开始,提取出 “ws://” URL 然后连接到该地址的服务 +* UI 应用马上识别出这个连接,并开始生成下一轮二维码测试 +* 展示出新的带有 “readyToStart?” 标记的二维码 +* 移动端读取二维码然后通过 WebSocket 发送确认信息 -![TXQR tester design](https://divan.dev/images/txqr_tester_design.png) +![TXQR 测试设计](https://divan.dev/images/txqr_tester_design.png) -So, in the end, I just have to put the mobile phone on the tripod, and let it talk to the app via scanning QR codes and send messages via WebSocket. +这样,最后,我只需要把移动电话放到架子上,让它通过扫描二维码发送信息并通过 WebSocket 发送信息和应用相互交流即可。 -![TXQR tester demo](https://divan.dev/images/txqr_tester.gif#center) +![TXQR 测试范例](https://divan.dev/images/txqr_tester.gif#center) -At the end UI allows to download CSV file with results, that can be analyzed in R or any other statistical tool or language. +终端 UI 支持下载 CSV 文件,基于这个文件,可以使用 R 或者其他统计工具和语言对其进行分析。 -# Benchmarking +# 基准测试 -The full testing cycle runs around 4 hours (the heaviest part – generation of animated QR runs in the browser, and as it’s still JS under the hood, it uses only one CPU core), and I had to make sure that the screen doesn’t turn itself off, or some other application window doesn’t cover the testing app. I set up testing with the following parameters: +完整的测试循环运行了大约 4 个小时(最花费时间的部分 —— 生成动态二维码是在浏览器运行的,依旧是使用的 JS,它只用了一个 CPU 内核),我还需要确保屏幕不会关闭,或者其他应用的窗口没有覆盖掉测试应用。我采用如下参数配置了测试: -* **FPS** - from 3 to 12 -* **QR frame size** - from 100 to 1000 (with step 50) -* **QR Recovery Levels** - all four -* **Data size** - 13KB (it’s randomly generated binary bytes) +* **FPS** —— 3 到 12 +* **二维码帧体积** —— 100 到 1000(步长 30) +* **二维码恢复级别** —— 所有级别,共 4 个 +* **数据体积** —— 13KB(数据是随机生成的二进制字节) -After a few hours, I downloaded CSV file and did some quick analysis and visualizations. +几个小时后,我下载了 CSV 文件并做了快速分析和可视化。 -# Results +# 结果 -A picture is worth a thousands words, but 3D interactive widgets is worth a thousand pictures. Here is 3D scatterplot for obtained results: +一张图像的信息量等同于千言万语,而三维可交互的小部件能提供的信息则相当于上千图像。如下是测试获取结果的 3D 散点图: [![qr_scan_results](https://plot.ly/~divan0/1.png?share_key=t8DizOL9dynI6NTcLA88Xi)](https://plot.ly/~divan0/1/?share_key=t8DizOL9dynI6NTcLA88Xi "qr_scan_results") -The best result was 1.4 secs, which is almost 9KB/s! This result has been recorded at a rate of 11 frames per second and chunk size of 850 bytes with Medium recovery level. In fact, however, at such encoding ratio and fps the probability of phone camera missing a frame was quite high, so in many cases it was just looping over and over, waiting for the missed frame until the test round timed out. +最佳结果是 1.4 秒,速度几乎到达 9KB/s!这个结果的速率是 11 帧每秒,数据块体积是 850 字节,采用中等恢复级别。事实上,在这个编码率和 fps 上,手机摄像机丢失帧的可能非常高,所以很多时候应用只是在不断循环,等待丢失的帧,直到测试循环的时间耗尽。 -Here are the boxplots for chunk size and fps variables (note, timed out measurements recorded here as values of 30s): +下面是数据块体积和 fps 变化时的条形图(注意,这里过期时间是 30s): -#### Time vs Chunk size: +#### 时间与数据块体积: [![Time vs Size](https://divan.dev/images/qr_size.png)](https://plot.ly/~divan0/3/) -As we can see above, low chunk sizes result in too much overhead for QR encoding and overall transferring time skyrockets. There are some sane values like 550 and 900 bytes per chunk, but step left or right, and we got timeouts due to missed frames again. 1000 bytes size is almost guaranteed miss of frames and timeout. +如上图所示,较小的数据块体积会导致二维码编码开销过大,并导致整体时间飙升。比较明智的取值是每个数据块 550 以及 900 字节,但是更高或者更低的字节都会由于丢失帧而导致超时。到了 1000 字节的大小,我们几乎可以肯定会丢失帧,并导致超时。 -#### Time vs FPS: +#### 时间与 FPS: [![Time vs FPS](https://divan.dev/images/qr_fps.png)](https://plot.ly/~divan0/2/) -FPS parameter, a bit to my surprise, didn’t have that much effect. The best values seem to be 6-7 FPS, which equals to approximately 150ms between frames. The lower fps result in increasing wait time, and faster FPS yields missed frames. +很令我吃惊,FPS 参数对结果并没有很大的影响。最佳取值似乎是 6-7 FPS,大约等于帧间隔 150ms。更低的 fps 会导致等待时间增加,而更高的 FPS 则导致帧丢失。 -#### Time vs QR Recovery Level: +#### Time 与二维码恢复级别 [![Time vs Lvl](https://divan.dev/images/qr_lvl.png)](https://plot.ly/~divan0/6/) -QR Recovery Level parameter showed a clear connection between transferring time and the redundancy level. The clear winner here is Low level (7% redundancy), and there is no surprise – smaller redundancy of data results in more readable and smaller QR codes, which are easier to scan and faster to process. For data transfer purpose we probably don’t need much redundancy at all. So safe values should be either Medium or Low. +二维码恢复级别参数和传输时间以及冗余级别都有很强的关联性,很明显,更好的选择是比较低的恢复级别(7% 的冗余),毫无疑问 —— 较少的冗余数据更容易读取,二维码体积也更小,也就更容易扫描和识别。对于数据传输,我们也许并不需要很多冗余。所以比较好的取值可以是中等或者低级就可以了。 -Now, those test rounds should probably be run hundreds of times more, on different screens and different devices, to make more adequate conclusions. But for my weekend exploring that was more than enough. +为了获取更丰富的结论,这些测试循环也许应该在不同屏幕和设备上运行上百次。但是对于我这个周末的研发,已经足够了。 -# Conclusion +# 结论 -This fun project proved to me that it’s definitely possible to add unidirectional data transfer without any network connectivity at all using animated QR codes. While maximum data transfer rate was around 9KB/s, in the vast majority of cases you can expect more modest rates – 1-2KB/s. +这个有趣的项目向我证明了,不需要任何网络连接,仅使用动态二维码的情况下,单向的数据传输是绝对可能的。并且最大的数据传输速率约为 9KB/s,绝大多数情况下的平均速率是 —— 1-2KB/s。 -I also had incredibly great and productive experience with both Gomobile and Gopherjs (with Vecty) – they practically become my usual daily tools. They’re mature, fast and gives you “it just works” experience. +同时,使用 Gomobile 和 Gopherjs(同时配合 Vecty) 也让我有了一段非常棒非常高效的研发体验 —— 它们几乎成为了我的日常开发工具。它们是成熟的框架,运行迅速并且能给你“它真的可以运行”的惊喜体验。 -Last, but not least, I’m still amazed at how productive you can be with Go, once you know what you’re going to build – extra short edit-run loop time boosts experimentation, simple code and no class-hierarchies madness makes refactoring easy and nice chore, and cross-platform mindset allows you to reuse the same code in a server, in a web app and in a mobile apps at the same time. There is also still plenty of room for the optimization and speedups, as I mostly did things in the most straightforward way. +最后,但是也同等重要的是,使用 Go 语言你可以大大提高效率,这一直都让我感到非常神奇,一旦你知道你需要构建什么 —— 附加简短的编辑运行循环时间却可以促进测试,简单的代码并且不存在让人发狂的类继承,这让重构成为简单流畅的工作,跨平台的设计思路让你能在服务端、web 应用和移动端应用同时复用相同的代码。同时还有大量可以优化和加速的空间,我只是用最直接的方式完成了工作。 -So if you have never tried gomobile or gopherjs – I encourage you to try it on the next opportunity. It’ll take an hour of your time to start playing with, but could open a whole new world of web or mobile development with Go. Give it a try! +如果你还从没尝试过 gomobile 或者 gopherjs —— 我建议你有机会尝试一下。它会需要你大概一个小时的时间来学习,但是能为你开启一扇能使用 Go 开发 web 或者移动端的世界的大门。去试试看吧! -## Links +## 参考链接 * [https://github.com/divan/txqr](https://github.com/divan/txqr) * [https://github.com/divan/txqr/tree/master/cmd/txqr-tester](https://github.com/divan/txqr/tree/master/cmd/txqr-tester) @@ -252,9 +252,9 @@ So if you have never tried gomobile or gopherjs – I encourage you to try it o * [https://github.com/gopherjs/vecty](https://github.com/gopherjs/vecty) * [https://github.com/golang/mobile](https://github.com/golang/mobile) -## Update +## 更新 -**UPD: txqr now uses much more efficient approach using [fountain codes](https://en.wikipedia.org/wiki/Fountain_code). See the [follow-up article](https://divan.dev/posts/fountaincodes/) with detailed description and results comparison.** +**更新:txqr 现在使用更加有效的方法即[喷泉码](https://en.wikipedia.org/wiki/Fountain_code)。[后续的文章](https://divan.dev/posts/fountaincodes/)里有详细讲解和测试结果比较,有兴趣可以查看。** > 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 From 1c67d83fec453cea0ac6f5bbfbc3eb39e20b8deb Mon Sep 17 00:00:00 2001 From: Lucas biu <517197934@qq.com> Date: Sat, 13 Jul 2019 11:50:15 +0800 Subject: [PATCH 31/68] =?UTF-8?q?=E4=BD=BF=E7=94=A8=20Swift=205=20?= =?UTF-8?q?=E6=9E=84=E5=BB=BA=E4=B8=80=E4=B8=AA=20iOS=20=E7=A7=BB=E5=8A=A8?= =?UTF-8?q?=E7=AB=AF=E7=BE=A4=E8=81=8A=E5=BA=94=E7=94=A8=E7=A8=8B=E5=BA=8F?= =?UTF-8?q?=20(#6084)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * translation/effiective BLoC pattern for flutter * feat: merge upstream master * Update how-to-build-ios-mobile-group-chat-app-swift-5-pubnub.md * Update how-to-build-ios-mobile-group-chat-app-swift-5-pubnub.md * Update how-to-build-ios-mobile-group-chat-app-swift-5-pubnub.md * Update effective-bloc-pattern.md * Update how-to-build-ios-mobile-group-chat-app-swift-5-pubnub.md --- ...os-mobile-group-chat-app-swift-5-pubnub.md | 228 +++++++++--------- 1 file changed, 114 insertions(+), 114 deletions(-) diff --git a/TODO1/how-to-build-ios-mobile-group-chat-app-swift-5-pubnub.md b/TODO1/how-to-build-ios-mobile-group-chat-app-swift-5-pubnub.md index f1d3a13bf8c..411eb4225db 100644 --- a/TODO1/how-to-build-ios-mobile-group-chat-app-swift-5-pubnub.md +++ b/TODO1/how-to-build-ios-mobile-group-chat-app-swift-5-pubnub.md @@ -2,95 +2,95 @@ > * 原文作者:[Samba Diallo](https://www.pubnub.com/blog/author/samba_diallo/) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/how-to-build-ios-mobile-group-chat-app-swift-5-pubnub.md](https://github.com/xitu/gold-miner/blob/master/TODO1/how-to-build-ios-mobile-group-chat-app-swift-5-pubnub.md) -> * 译者: -> * 校对者: +> * 译者:[LucaslEliane](https://github.com/lucasleliane) +> * 校对者:[江五渣](http://jalan.space),[Endone](https://github.com/Endone) -# How to Build an iOS Mobile Group Chat App with Swift 5 +# 使用 Swift 5 构建 iOS 移动端群聊 App -[Mobile chat](https://www.pubnub.com/solutions/chat/) of all shapes and sizes, whether it be a standalone [group messaging app](https://www.pubnub.com/solutions/chat/), an embedded customer service widget, or [private 1:1 chat in a dating app](https://dev-www.pubnub.com/solutions/chat/dating-apps/), is everywhere. +无论是独立的[群聊应用](https://www.pubnub.com/solutions/chat/),嵌入式的客户服务组件,或者是[约会应用里面的私人一对一聊天](https://dev-www.pubnub.com/solutions/chat/dating-apps/),各种特征和规模的[移动端聊天](https://www.pubnub.com/solutions/chat/)无处不在。 -In this tutorial, we’ll show you how to create an iOS mobile chat app with Swift 5, allowing any number of users to chat in realtime. We’ll also show you how to store message history, so when a user leaves and returns, their messages are still in the app. +在本教程中,我们将向你展示如何使用 Swift 5 构建一个 iOS 移动聊天应用程序,其可以让任意数量的用户进行实时聊天。我们还将向你展示如何存储消息历史记录,因此当用户离开之后回来时,他们的消息仍然在应用程序中。 -To create this, we will use a few key features of PubNub: **[Publish/Subscribe](https://www.pubnub.com/developers/tech/key-concepts/publish-subscribe/)** (realtime messaging)and [**Storage & Playback**](https://www.pubnub.com/developers/tech/key-concepts/message-caching-persistence/) (message storage). +为了实现上述的应用,我们使用了 PubNub 的一些关键特性:**[发布/订阅](https://www.pubnub.com/developers/tech/key-concepts/publish-subscribe/)**(实时消息)和 **[存储 & 回放](https://www.pubnub.com/developers/tech/key-concepts/message-caching-persistence/)(消息存储)**。 -* **Publishing** is how each client gets its message out to the world, or at least to the channel that it publishes to. It’s the beginning of using Pub/Sub, each message you send will be delivered to anyone subscribed to the channel you publish to. Publishing requires an instance of a PubNub connection (I’ll go into more detail later), the message (of types String, NSNumber, Array, and Dictionary), and the channel we want to send our message to. [Find out more on Publishing in Swift.](https://www.pubnub.com/docs/swift/api-reference-publish-and-subscribe#publish) -* **Subscribing** is the second half of communicating instantly via PubNub. In order to subscribe, we’ll need an instance of a PubNub connection and a channel to subscribe to. After successfully subscribing, we will receive messages, but we still won’t see them as we have nothing to handle when they arrive. [Find out more on Subscribing in Swift.](https://www.pubnub.com/docs/swift/api-reference-publish-and-subscribe#subscribe) -* **Event Handling or listening** for updates is quite important in the life cycle of PubNub. Pub/Sub takes the spotlight but the hidden champions of using PubNub are the event handlers that connect the [Data Stream Network](https://www.pubnub.com/products/global-data-stream-network/) to our console and app. One that specifically listens for messages and another that looks out for anything else including subscription changes and errors. -* **Storage & Playback** is another great addition to a roster chock full of features. If storing and retrieving messages is import to your use case, Storage & Playback is also a great addition to your app. Its functions allow for retrieval of historic messages. The time-to-live for an app’s messages range from the beginning of time to within the last day. We will set the time-to-live of storage in the PubNub Admin Dashboard once we set up our PubNub account and get our API keys. [Find out more on Storage & Playback in Swift](https://www.pubnub.com/docs/swift/api-reference-storage-and-playback). +* **发布**是每个客户端如何将自己的消息发送到全世界的方式,或者至少传递到自己想要发布的频道中。Pub/Sub 模式最简单的应用就是将你发送的每一条消息传递给订阅频道的任何人。发布需要一个 PubNub 连接的实例(我将在后面详细介绍),要发送的消息消息(类型为 String、NSNumber、Array 和 Dictionary),以及我们要将消息发送到的频道。[了解有关 Swift 发布的更多信息。](https://www.pubnub.com/docs/swift/api-reference-publish-and-subscribe#publish) +* **订阅**是 PubNub 即时通信的另外一个部分。为了订阅,我们需要一个 PubNub 连接的实例和一个要订阅的频道。成功订阅后,我们会收到消息,但如果我们在消息到达的时候不进行处理,我们仍然看不到这些消息。[了解有关 Swift 订阅的更多信息](https://www.pubnub.com/docs/swift/api-reference-publish-and-subscribe#subscribe)。 +* **事件处理或监听**的更新在 PubNub 的生命周期中非常重要。Pub/Sub 虽然非常引人注目,但使用 PubNub 的关键是事件处理程序,它将[数据流网络](https://www.pubnub.com/products/global-data-stream-network/)连接到我们的控制台和应用程序。它们中的一个专门监听消息,而另一个负责寻找其他任何内容,包括订阅变动和错误。 +* **存储和回放**是这个伟大的功能集的另外一个关键点。如果存储和消息检索已导入你的工程,那么存储和播放对你的应用程序来说也是很好的补充。这个功能允许检索历史消息。应用程序消息的范围囊括了应用程序的整个生命周期。我们将设置 PubNub 帐户并获取 API 密钥,在 PubNub 管理控制台中设置存储的生存时间。[了解有关 Swift 中存储和回放的更多信息](https://www.pubnub.com/docs/swift/api-reference-storage-and-playback)。 -After this tutorial, you will have an app that provides a chatroom service and is a great base and addition for any app. +在看完这个教程之后,你会实现一个提供了聊天室服务的应用,并且这个应用可以是其他任何应用程序很好的基础或者补充。 -**The [full iOS chat app with Swift 5 is available here](https://github.com/SambaDialloB/PubNubChat).** +**[完整的 Swift 5 iOS 聊天应用程序可以在这里找到](https://github.com/SambaDialloB/PubNubChat)**。 -## Setup +## 构建 ### PubNub -If you haven’t yet, [sign up for a PubNub account](https://dashboard.pubnub.com/signup). Once you’re logged in, create a new app. Click on it and either create a new keyset or click on the demo one available already. You should now see a publish and subscribe keys, these are what allow us to use the PubNub API. +如果你还没有 PubNub 账户,[可以在这里注册一个帐户](https://dashboard.pubnub.com/signup)。登录后,创建一个新的应用程序。单击它并创建一个新的密钥集或单击已有的演示版。 你现在应该看到发布和订阅密钥,我们可以通过其使用 PubNub API。 -Under the keys, we have different options that we can enable! Let’s enable Storage & Playback near the bottom left. We’re at the point where you can decide how long you want your messages to be retained for. I chose a one-day retention length and saved the changes. Under the retention setting, there are settings to enable [delete from PubNub history](https://www.pubnub.com/docs/swift/api-reference-storage-and-playback#delete-messages-from-history). +在 keys 下,我们可以启用不同的选项!让我们在左下角附近启用存储和回放功能。我们现在可以决定你希望保留多长时间的消息。我选择了一天的保留时长并保存了更改。在保留设置下,还可以设置启用[从 PubNub 历史记录中删除](https://www.pubnub.com/docs/swift/api-reference-storage-and-playback#delete-messages-from-history)。 -### Xcode App Setup +### Xcode 应用构建 -Open up Xcode and create a new project, make it a Single View App, name it, and then close the project. Navigate to your project folder using Terminal and either run the command gem install cocoapods or gem update cocoapods if you already have cocoapods installed. +打开 Xcode 并创建一个新项目,选择单视图应用程序,给他起一个名字,然后关闭项目。使用终端导航到项目文件夹,运行命令 `gem install cocoapods` 或运行命令 `gem update cocoapods` 来更新已有的安装。 -Enter touch Podfile into your terminal to create the Podfile for your app then go ahead and open the file using open Podfile. +在终端中输入 `touch Podfile`,为你的应用创建 Podfile,然后使用 `open Podfile` 打开文件。 -Enter this into the file, making sure to replace the “application-target-name” with that of your project. +将下面的代码写入到文件中,确保将“application-target-name”替换为项目的名称。 ```swift source ‘https://github.com/CocoaPods/Specs.git' -# optionally complete and uncomment if compilation issues arise +# 如果出现编译问题,可以选择取消下面的注释并且填写完整 # project ‘/’ # workspace ‘MyPubNubProject’ use_frameworks! -# replace next lines name in quote with your project name +# 用你的项目名称替换下一行中的引号里面的内容 target ‘application-target-name’ do -# Should only use this with projects -# that must have a minimum deployment -# target of iOS 8 +# 下面的配置只适用于 +# 最小编译目标为 +# iOS 8 的项目 platform :ios, ‘8.0’ # (or ‘9.0’ or ‘10.0’) pod “PubNub”, “~> 4” -end + ``` -Run the command pod install in your terminal after. This should install the PubNub framework for you in your project. After it is installed, open up your project by double-clicking the .xcworkspace file instead of your normal project file. +之后在终端中执行命令 `pod install`。这个命令会帮你在项目中安装 PubNub。安装完成之后,双击 .xcworkspace 文件可以打开项目工程。 -## Designing the App +## 设计应用程序 -Before we start putting in all the logic, let’s design and build up the views of our app. Let’s start with our login view. +在我们开始介绍所有逻辑之前,让我们先设计并构建应用程序的视图。首先我们从登录视图开始。 -Rename ViewController.swift to ConnectVC.swift by highlighting the name in the class declaration and going up to Editor -> Refactor -> Rename. +通过高亮点击类声明中的名字,将 ViewController.swift 重命名为 ConnectVC.swift,并且进入 Editor -> Refactor -> Rename。 -When a user opens the app, we want them to have a field to enter a username and a channel they want to connect to, in addition to a connect button. Add those to your first view. Also, put in a cool title you want to call this app, I chose Topically! +当用户打开应用程序时,除了连接按钮外,我们希望他们有一个字段来输入他们想要连接的用户名和频道。将这些添加到你的第一个视图中。另外,我选择了 Topically 作为我们应用程序的标题,你也可以选择一个更酷的标题。 -I then made outlets for my username and channel TextFields by control-dragging from the item on my storyboard to my ConnectVC file. Do the same to your button, but instead of an outlet, create an action, of type UIButton, for it to run when pressed. +然后,我通过 control + 拖动的方式,将我的 storyboard 上的项目拖动到我的 ConnectVC 文件,来为我的用户名和频道的 TextFields 设置 outlet。对按钮执行相同操作,但不要使用 outlet,而是创建 UIButton 的 action,以便在按下时执行操作。 -![Xcode screenshot swift storyboard chat app with PubNub](https://www.pubnub.com/wp-content/uploads/2019/04/xcode-storyboard-swift-chat-pubnub.png) +![使用 PubNub 的聊天应用程序的 Xcode Swift storyboard 截图](https://www.pubnub.com/wp-content/uploads/2019/04/xcode-storyboard-swift-chat-pubnub.png) -Next, let’s create our channel chat view. +接下来,让我们创建频道聊天视图。 -Create a new Cocoa Touch Class and name it ChannelVC. Create a new view controller in your storyboard and set the class to ChannelVC as well. While selecting that view, go to the top of your screen and click Editor -> Embed In -> Navigation Controller. Another view should now be in your storyboard. This is the navigation controller, it allows for more mobility for users when going in between views. +创建一个新的 Cocoa Touch 类并将其命名为 ChannelVC。在 storyboard 中创建一个新的视图控制器,并将该类设置为 ChannelVC。选择该视图时,请转到屏幕顶部,然后单击 Editor -> Embed In -> Navigation Controller。另一个视图现在应该在你的 storyboard 中。这是导航控制器,它允许用户在进入视图之间切换。 -Add a UIBarButtonItem to the left spot on the navigation bar on your ChannelVC, this will be the leave button. Control-drag that to your ChannelVC.swift and create an action of type UIBarButtonItem named leaveChannel. Drag a UITableView into your ChannelVC view. Make it take up most of the screen leaving enough room for another TextField and a button with the text Send in it. Create those as well. +将一个 UIBarButtonItem 添加到 ChannelVC 导航栏上的左侧位置,这是我们的“离开”按钮。按住 Control 键并将其拖到 ChannelVC.swift,并创建名为 leaveChannel,UIBarButtonItem 类型的 action。将 UITableView 拖到 ChannelVC 视图中。使其占据屏幕的大部分空间,但需要流出空间放置另一个 TextField 和一个带有文本 Send 的按钮。创建它们。 -Make outlets for the table and for the TextField in your ChannelVC.swift, and make another action for the send button. +在 ChannelVC.swift 中为 table 和 TextField 创建 outlet,并为发送按钮添加另一个 action。 -Our next step involves not our ChannelVC, but creating the custom cell inside of our table. Once we have the general layout of our ChannelVC setup, we have to customize the cells in our tableView. Create a new cocoa touch class named MessageCell, and drag a UITableViewCell into your table view. Set that cells class to your new class and change the identifier to MessageCell as well. +我们的下一步不涉及我们的 ChannelVC,而是在我们的 table 内创建自定义单元格。一旦我们得到 ChannelVC 设置的总体布局,我们就必须在 tableView 中自定义单元格。创建一个新的 cocoa touch 类并且命名为 MessageCell,并将 UITableViewCell 拖到表视图中。将该 cell 类设置为新类,并将标识符更改为 MessageCell。 -Drag whatever design you want, including whatever details you deem necessary for your use case. I put in the username and message labels into the cell, and once you’re done, control-drag to create outlets to your MessageCell class. Make sure to set constraints so that the table doesn’t squish your content. +拖动任何东西来完成你想要的设计和需要的任何细节。我们将用户名和消息标签放入 cell 中,完成之后,按住 Ctrl 键拖动即可为 MessageCell 类创建 outlet。确保设置样式约束,以便表格不会压缩你的内容。 -**For more information on making your app work on all screen sizes, reference [Apple’s documentation on Auto Layout](https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/AutolayoutPG/index.html) or the numerous guides available online.** +**有关使你的应用程序适用于所有屏幕尺寸的更多信息,请参阅 [Apple 关于自动布局的文档](https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/AutolayoutPG/index.html)或者查阅众多的在线指南。** -Having all of these views is great, but not if they cannot move from one to another. Click on the bar above ConnectVC that has its name, and then click the yellow circle. Control-drag this to the navigation controller and select the show option. While the line to nav controller is selected click on the attributes tab on your right panel where it says “Storyboard Segue” at the top. Name the identifier “connectSegue”. This will allow you to perform this segue when you click the connect button on ConnectVC. +现在我们得到很多不错的 view 视图,但它们之间无法进行自由切换。单击 ConnectVC 上方有其名称的栏,然后单击黄色圆圈。按住 Control 键并将其拖动到导航控制器并选择 show 选项。选择导航控制器,单击右侧面板上的属性选项卡,其顶部显示“Storyboard Segue”。将标识符命名为“connectSegue”。当你单击 ConnectVC 上的连接按钮时,就可以执行这个 Segue 了。 -The next and final segue we need is one that brings us from ChannelVC to ConnectVC. Select ChannelVC the same way we did to ConnectVC and drag it to ConnectVC. This time select “Present Modally” and name it “leaveChannelSegue” in the attributes inspector. +我们需要的下一个也是最后一个任务是将我们从 ChannelVC 导航到 ConnectVC。选择 ChannelVC 的方式与 ConnectVC 相同,并将其拖到 ConnectVC。这次选择“Present Modally”并在属性检查器中将其命名为“leaveChannelSegue”。 -![Storyboard Xcode for PubNub Chat](https://www.pubnub.com/wp-content/uploads/2019/04/xcode-storyboard-screenshot-chat-pubnub.png) +![PubBub 聊天程序的 Xode Storyboard](https://www.pubnub.com/wp-content/uploads/2019/04/xcode-storyboard-screenshot-chat-pubnub.png) -## Coding: ConnectVC +## 编码:ConnectVC -Now that we’ve finished with the storyboard, let’s start coding. We’ll start with ConnectVC, which provides the username and channel to our ChannelVC where we’ll utilize all of our PubNub knowledge. To start, perform a segue in our connect action. +现在我们已经完成了 storyboard,让我们开始编码。我们将从 ConnectVC 开始,它为我们的 ChannelVC 提供用户名和频道,我们将利用我们所有的 PubNub 知识。首先,在我们的连接操作中执行 segue。 ```swift @IBAction func connectToChannel(_ sender: UIButton) { @@ -98,22 +98,22 @@ Now that we’ve finished with the storyboard, let’s start coding. We’ll sta } ``` -This utilizes our connectSegue we made in the previous section that brings us to the ChannelVC’s nav controller. The only other thing we need to do in this view controller is to prepare for the segue above. By overriding this function, we send information between the views. +这利用了我们在上一节中制作的 connectSegue,它将我们导航到了 ChannelVC 的导航控制器。我们在这个视图控制器中唯一需要做的就是为上面的 segue 做准备。通过重写这个功能,我们可以在视图之间发送信息。 -****NOTE:** In this tutorial, if the user does not provide a username, I automatically assign the name “A Naughty Moose” to them. If they do not provide a channel, I send them to the channel “General.”** +**注意:在本教程中,如果用户未提供用户名,我会自动为其分配用户名“A Naughty Moose”。如果他们没有提供频道,我会将他们发送到频道“General”。** -To access the view we are going to, we get an instance of our navigation controller, then get our ChannelVC view from there. We check if the text fields are empty, replace the values if we need to, and then set two variables in ChannelVC, which we have not created yet, with our username and channel values. +为了访问我们想要访问的视图,我们需要获得导航控制器的实例,然后从那里获取我们的 ChannelVC 视图。我们检查文本字段是否为空,如果需要则替换值,然后使用我们的用户名和频道在 ChannelVC 中设置两个我们尚未创建的变量。 ```swift override func prepare(for segue: UIStoryboardSegue, sender: Any?) { - //Accessing Nav Controller and ChannelVC view + // 访问导航控制器和 ChannelVC 视图 if let navigationController = segue.destination as? UINavigationController, let channelVC = navigationController.viewControllers.first as? ChannelVC{ var username = "" var channel = "" - //Replacing empty values with default ones + // 下面的空字符串替换成一个你需要的默认值 if(usernameTextField.text == "" ){ username = "A Naughty Moose" } @@ -128,31 +128,31 @@ override func prepare(for segue: UIStoryboardSegue, sender: Any?) { channel = channelTextField.text ?? "General" } - //Setting values in ChannelVC + // 设置 ChannelVC 的变量值 channelVC.username = username channelVC.channelName = channel } } ``` -## Coding: ChannelVC +## 编码:ChannelVC -In our ChannelVC, we should have two outlets, an action and our viewDidLoad function inside already. Above all of those, under the class definition, we will start defining some variables and resources that we need for the rest of the class. +在我们的 ChannelVC 中,我们应该有两个 outlet,一个 action,另一个是我们的 viewDidLoad 函数。最重要的是,在类定义下,我们将开始为类的其余部分定义一些我们需要的变量和资源。 -First, let’s make our class listen for PubNub events and make it work with our table. Import PubNub at the top of your file and after UIViewController in our class definition put in PNObjectEventListener, UITableViewDataSource, and UITableViewDelegate. Our class should now show an error, click on the error and add those stubs suggested, we’ll fill those out in just a second. +首先,让我们的类监听 PubNub 事件并使其与我们的 table 一起工作。在文件顶部引入 PubNub,在我们的类定义中的 UIViewController 写入 PNObjectEventListener、UITableViewDataSource 和 UITableViewDelegate 之后。我们的类现在应该显示错误,单击错误并添加建议的那些类,这样进行引入比较便捷。 -* Right under our class definition, let’s define a struct that makes working with our messages a little easier. My struct has three strings: a message, a username, and a UUID. Later when we are publishing messages, you can send different information and update the struct with those updates. -* After that, create an array of Message and initialize it to be empty as all class variables need to have some sort of initial value. -* Create a marker of the earliest message we have received, of type NSNumber and start the marker as -1. -* Another variable to keep track of whether we already started loading more messages. -* Now for the most important variable of this view controller, the one that publishes and subscribes, our PubNub object! This is the object we will be calling our PubNub functions on in this view controller. -* We then have the username and the channel that the user entered in the last step, initialized with temporary values. -* After that should be our message text field, our tableView, and our and our send action. +* 在我们的类定义下,让我们定义一个结构,使得处理我们的消息更容易一些。我的结构有三个字符串:消息,用户名和 UUID。稍后当我们发布消息时,你可以发送不同的信息并使用这些来更新结构。 +* 之后,创建一个 Message 数组并将其初始化为空,因为所有类变量都需要具有某种初始值。 +* 为我们最早收到的消息创建一个 NSNumber 类型的标记,并设置标记的初始值为 -1。 +* 另一个变量,用于跟踪我们是否已开始加载更多消息。 +* 现在,对于这个视图控制器来说,最重要的、用于发布和订阅的变量,是我们将在此视图控制器中调用 PubNub 函数的对象。 +* 然后我们得到用户在最后一步中输入的用户名和频道,并使用临时值进行初始化。 +* 之后应该是我们的消息文本字段,我们的 tableView,以及我们的发送 action。 ```swift class ChannelVC: UIViewController,PNObjectEventListener, UITableViewDataSource, UITableViewDelegate { - //Our Message struct, makes working with messages a little easier + // 我们的消息结构体,可以让消息的处理更方便 struct Message { var message: String var username: String @@ -160,24 +160,24 @@ class ChannelVC: UIViewController,PNObjectEventListener, UITableViewDataSource, } var messages: [Message] = [] - //Keep track of the earliest message we loaded + // 跟踪我们加载的最早的一条消息 var earliestMessageTime: NSNumber = -1 - //To keep track if we are already loading more messages + // 来跟踪我们是否已经加载了更多消息 var loadingMore = false - //Our PubNub object that we will use to publish, subscribe, and get the history of our channel + // 我们使用 PubNub 对象来发布,订阅和获取我们频道的内容 var client: PubNub! - //Temporary values + // 临时值 var channelName = "Channel Name" var username = "Username" - //-- ALREADY SHOULD BE IN YOUR FILE - //Where our messages come in + //-- 应该已经存在在你的文件里面了 + // 消息入口 @IBOutlet weak var messageTextField: UITextField! - //We populated this with the information from our messages array + // 我们用来自 messages 数组的信息填充了这个 View @IBOutlet weak var tableView: UITableView! //... @@ -185,7 +185,7 @@ class ChannelVC: UIViewController,PNObjectEventListener, UITableViewDataSource, } ``` -Next, that we have established some global variables that we can use all throughout our code, let’s set up our viewDidLoad function. After calling the inherited viewDidLoad, change the title at the top of the navigation controller to the channel name and set your table view’s delegate and data source to self. +我们已经建立了一些可以在整个代码中使用的全局变量,接下来,让我们设置 viewDidLoad 函数。在调用继承的 viewDidLoad 之后,将导航控制器顶部的标题更改为频道名称,并将 table view 的 delegate 数据源设置为 self。 ```swift self.navigationController?.navigationBar.topItem?.title = channelName @@ -194,33 +194,33 @@ tableView.delegate = self tableView.dataSource = self ``` -Next, we configure and initialize our PubNub object. Here is where you insert your publish and subscribe keys found in your PubNub account.  We set stripMobilePayload to false as it is deprecated and give this connection a unique UUID, which will make it easier to develop more features in the future. We initialize it, set ourselves as a listener, and subscribe to the channel the user picked. We then call the method loadLastMessages which we will create next. +接下来,我们配置并初始化我们的 PubNub 对象。你可以在此处插入 PubNub 帐户中的发布和订阅密钥。我们将 stripMobilePayload 设置为 false,因为它已弃用,并为此连接提供唯一的 UUID,这使我们在将来更容易开发更多功能。接着初始化它,将它设置为监听器,并订阅用户选择的频道。然后我们调用将在下一步创建的方法 loadLastMessages。 ```swift -//Setting up our PubNub object! +// 设置我们的 PubNub 对象! let configuration = PNConfiguration(publishKey: "INSERT PUBLISH KEY", subscribeKey: "INSERT SUBSCRIBE KEY") -//Gets rid of deprecated warning +// 删除已经弃用的警告 configuration.stripMobilePayload = false -//Making each connection identifiable for future development +// 给每个连接设置标志以供将来进行开发 configuration.uuid = UUID().uuidString client = PubNub.clientWithConfiguration(configuration) client.addListener(self) client.subscribeToChannels([channelName],withPresence: true) -//We load the last messages to populate the tableview +// 我们加载最后的消息来填充 tableview loadLastMessages() ``` -There should be an error right now, saying that the function we are calling at the end of our viewDidLoad is undefined, so let’s define it! This function is used to load the initial messages when we connect to a channel. It utilizes another function we will create named addHistory. +现在应该有一个 error,说我们在 viewDidLoad 末尾调用的函数是未定义的,所以让我们定义它!此功能用于在连接到通道时加载初始消息。它利用我们即将创建的,名为 addHistory 的另一个函数。 -Let’s call this next function, using nil for the start and end, and then set a number of messages you would like to receive, to a max of 100. Our final act inside of this function is to bring our table views scroll down to the bottom of the table to the new messages. +让我们调用下一个函数,使用 nil 作为开始和结束,然后设置你想要接收的消息的数量,最多为 100。我们在函数内部的最后一个操作是将我们的 table view 向下滚动到表格底部的新消息。 ```swift -//This function is called when this view initialy loads to populate the tableview +// 当这个视图初始化加载来填充 tableview 的时候,将调用此函数 func loadLastMessages() { addHistory(start: nil, end: nil, limit: 10) - //Bring the tableview down to the bottom to the most recent messages + // 将 tableview 滚动到最新消息的底部 if(!self.messages.isEmpty){ let indexPath = IndexPath(row: self.messages.count-1, section: 0) self.tableView.scrollToRow(at: indexPath, at: .bottom, animated: true) @@ -228,41 +228,41 @@ func loadLastMessages() } ``` -### Storage and Playback for Message History +### 存储和回放历史消息 -Now for the function that allows us to look back into our history of the channel. Create it with a few key parameters, only of which the limit is needed, then call the essential function that allows us to view our channel’s history. +现在,我们可以通过这个功能回顾我们频道的历史记录。使用一些关键参数创建它,只有 limit 参数是必须的,然后调用函数就可以允许我们查看频道历史记录了。 -We use the function historyForChannel which has many overloaded versions. We could use the simpler one that returns the last 100 messages or one that takes in start and end times, both of these methods are handled by a PNHistoryResultBlock, which allows us to access the result of the query and the errors. +我们使用具有许多重载版本的函数 historyForChannel。我们可以使用返回最后 100 条消息的简单消息或者接收开始和结束时间的消息,这两种方法都由 PNHistoryResultBlock 处理,这允许我们访问查询结果和 error。 -First, let’s check if the result is not nil and if the status is and start accessing the messages! Once we know that our messages contain at least something, we can start accessing them. We need to update our earlistMessage start time with the earliest message we have received within our result. Next converting the object we get back to something that we can manage, an array of String keys and values. +首先,让我们检查结果是否为非空,如果是,我们就可以开始访问消息了!一旦我们知道我们的消息至少包含某些内容,我们就可以开始访问它们。我们需要使用我们在结果中收到的最早消息来更新我们的 earlistMessage 开始时间。接下来将我们返回的对象转换为我们可以使用的的对象,一个键值为 String 类型的数组。 -From this new object, instead of working hard to access them every time we want to, let’s create a Message object from our struct, add them to a temporary array, then insert that at the beginning of our global messages array. Make sure to reload the table then check for errors! +我们可以从这个新对象创建一个 Message 对象,将它们添加到一个临时数组中,然后将它插入到我们的全局消息数组的开头而不是每次都去费力地直接访问这些对象。确保重新加载表格然后检查错误! ```swift -//Get and put the histroy of a channel into the messages array +// 获取并且将频道的历史消息放入到 messages 数组中 func addHistory(start:NSNumber?,end:NSNumber?,limit:UInt){ - //The PubNub Function that returns an object of X messages, and when the first and last messages were sent. - //The limit is how many messages are received with a maximum and default of 100. + // PubNub 函数,它返回 X 消息的对象,然后发送第一个和最后一个消息的时间 + // limit 是接收的消息数量,最大值为 100,默认值为 100 client.historyForChannel(channelName, start: start, end: end, limit:limit){ (result, status) in if(result != nil && status == nil){ - //We save when the earliest message was sent in order to get ones previous to it when we want to load more. + // 当我们想要加载更多的时候,我们会保存最早发送的消息的时间,以便获取之前的消息。 self.earliestMessageTime = result!.data.start - //Convert the [Any] package we get into a dictionary of String and Any + // 将我们获得的 [Any] 包转换为 String 和 Any 的 dictionary let messageDict = result!.data.messages as! [[String:String]] - //Creating new messages from it and putting them at the end of messages array + // 从中创建新的消息并且将它们放在消息数组的末尾 var newMessages :[Message] = [] for m in messageDict{ let message = Message(message: m["message"]! , username: m["username"]!, uuid: m["uuid"]! ) newMessages.append(message) } self.messages.insert(contentsOf: newMessages, at: 0) - //Reload the table with the new messages and bring the tableview down to the bottom to the most recent messages + // 使用新的消息重新加载 tableview,并且将 tableview 向下滚动到最新消息的底部 self.tableView.reloadData() - //Making sure that we wont be able to try to reload more data until this is completed. + // 确保在加载完成之前,我们无法尝试重新加载更多数据 self.loadingMore = false } else if(status != nil){ @@ -276,12 +276,12 @@ func addHistory(start:NSNumber?,end:NSNumber?,limit:UInt){ } ``` -Next, let’s fill in our tableView required functions. The first, numberOfRowsInSection is an easy one-liner, return the number of messages in your array. For the second, we first need to get an instance of your message cell and set the cell labels’ text to the message and username of the index of our message array. After that, just return the cell! +接下来,让我们填写 tableView 所需的函数。第一个,numberOfRowsInSection 是一个简单的单行函数,返回数组中的消息数。第二个函数,我们首先需要获取消息 cell 的实例,并将 cell 标签的文本设置为消息和消息数组索引的用户名。然后,直接返回 cell 就可以了! ```swift -//Tableview functions required. +// 需要 tableview 函数 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - //change later + // 后面修改 return messages.count } @@ -296,16 +296,16 @@ func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> U } ``` -One of the most import parts of using and debugging PubNub in Swift is creating listeners for events and messages. In this application, we use the function didRecieveMessage which allows us to access messages coming into our channel. The logic inside of this function will act as a smaller version of our loadLastMessages. +在 Swift 中使用和调试 PubNub 最重要的部分之一就是为事件和消息创建监听器。在这个应用程序中,我们使用函数 didRecieveMessage,这个函数允许我们访问进入到我们频道的消息。此函数内部的逻辑就是我们的 loadLastMessages 函数的精简版本。 -Check if the message that came in matches the channel that we are subscribed to in case we’re subscribed to anything else. Take the message we are given, and turn it into an array of String keys and values. Create a Message with that dictionary and append it to the end of your messages array. +检查进来的消息是否与我们订阅的频道匹配,以防我们订阅到其他频道的内容。获取我们给出的消息,并将其转换为键值类型为 String 的数组。使用该 dictionary 创建消息并将其绑定到消息数组的末尾。 -Again, reload the data, and scroll down to the new message. This action can be changed based on your use case. I print the message in my console for debugging. +再次重新加载数据,然后向下滚动到新消息。可以根据你想要的实现更改这个操作。我在控制台中打印消息以便进行调试。 ```swift func client(_ client: PubNub, didReceiveMessage message: PNMessageResult) { - //Whenever we receive a new message, we add it to the end of our messages array and - //reload the table so that it shows at thebottom. + // 每当我们收到一条新的消息时,我们都会将它添加到我们消息数组的末尾 + // 重新加载 table,以便消息展示在底部 if(channelName == message.data.channel) { @@ -323,19 +323,19 @@ func client(_ client: PubNub, didReceiveMessage message: PNMessageResult) { } ``` -Now we can load some of the last messages when first opening the channel, and when new ones are sent they appear at the bottom. +现在我们可以在第一次打开频道时加载一些历史消息,当发送新消息时,它们会显示在底部。 -What if there are more messages than what we initially load? In this new function, scrollViewDidScroll, we pull another amount of messages from historyForChannel when we pull down from the top. This can also be modified so that it will try to retrieve more when the user has not reached the top of the page for a more infinite scroll look. +如果有比我们最初加载的消息更多的消息怎么办?在这个新函数 scrollViewDidScroll 中,当我们从顶部向下拉时,我们从 historyForChannel 中提取了另外一些消息。这个函数也可以修改,以便当用户没有到达页面顶部时,它可以进行预加载,来帮助实现一个无限滚动的效果。 -We have a global variable called loadingMore that we check at the beginning to see if we are already loading more messages, and we then check if the user is scrolling past a certain threshold to start loading more. Thankfully using PubNub is ludicrously fast so it loads almost instantly. Once that is true, we set loadingMore to true and start to call our addHistory function, putting earliestMessageTime as the start, nil for the end, and the limit as however many you like, although the maximum that will be returned in 100. +我们有一个名为 loadingMore 的全局变量,我们在开始时检查是否已经加载了更多消息,然后检查用户是否滚动超过某个阈值来开始加载更多消息。值得庆幸的是,使用 PubNub 非常快,所以几乎可以立即加载。一旦真的有更多历史消息,我们将 loadingMore 设置为 true 并开始调用我们的 addHistory 函数,将 earliestMessageTime 作为开始,将 nil 作为结束,可以根据你的需求来设置 limit,尽管返回消息的最大值是 100。 ```swift -//This method allows users to query for more messages by dragging down from the top. +// 这个方法允许用户通过从顶部向下拖动来查询更多消息 func scrollViewDidScroll(_ scrollView: UIScrollView){ //If we are not loading more messages already if(!loadingMore){ - //-40 is when you have dragged down from the top of all the messages + // 当从消息顶部向下拖动超过 -40 的时候 if(scrollView.contentOffset.y < -40 ) { loadingMore = true addHistory(start: earliestMessageTime, end: nil, limit: 10) @@ -345,9 +345,9 @@ func scrollViewDidScroll(_ scrollView: UIScrollView){ } ``` -We now need to publish messages when we click the send button. For that to happen let’s create a function that sends the message that is in the messageTextField. Check if it is empty first, and handle if it is, then creates a dictionary with the message information you would like to send, and then use the simple publish function on your PubNub object. +我们现在需要在单击“发送”按钮时发布消息。为此,让我们创建一个函数来发送 messageTextField 中的消息。首先,我们检查 messageTextField 是否为空,如果是,则进行处理,然后创一个 dictionary 用于包含你要发送的消息信息,之后在 PubNub 对象上使用简单发布功能。 -This function takes in numerous types of variables and objects to send as the message and the channel name. You can also include a handler afterward to do something based on the status codes. After that, call the function that we just created in our sendMessage action. +这个函数接收多种类型的变量和对象作为消息和频道名称发送。你也可以在回调中包含一个处理程序,以根据状态代码执行某些操作。之后,调用我们刚刚在 sendMessage 操作中创建的函数。 ```swift func publishMessage() { @@ -368,30 +368,30 @@ func publishMessage() { } -//When the send button is clicked, the message will send +// 单击发送按钮的时候,将会发送消息 @IBAction func sendMessage(_ sender: UIButton) { publishMessage() } ``` -In order to make our app fully work, we need to be able to leave the channel and go back to the ConnectVC. We already have a function for that, we just need to fill it in. Unsubscribe from all channels the client is subscribed to and then perform the “leaveChannelSegue” that we created originally. +为了使我们的应用程序完全正常工作,我们需要能够离开频道并返回 ConnectVC。我们已经有了这个功能,我们只需要填写它。取消订阅客户订阅的所有频道,然后执行我们最初创建的“leaveChannelSegue”。 ```swift client.unsubscribeFromAll() self.performSegue(withIdentifier: "leaveChannelSegue", sender: self) ``` -## Running the App +## 运行 App -Let’s run the app! +让我们来运行一下这个应用程序! -![Swift Chat App with PubNub](https://www.pubnub.com/wp-content/uploads/2019/04/pubnub-swift-chat.gif) +![PubNub Swift 聊天应用程序](https://www.pubnub.com/wp-content/uploads/2019/04/pubnub-swift-chat.gif) -We’ve now got basic chat functionality. Users can send and receive messages in realtime, and messages are stored for a predefined amount of time. +我们现在拥有了基本的聊天功能。用户可以实时发送和接收消息,并且历史消息可以被存储一段时间。 -The full [GitHub repo for this project is available here](https://github.com/SambaDialloB/PubNubChat). +[这个项目完整的 Github 仓库在这里](https://github.com/SambaDialloB/PubNubChat) -Use PubNub for free up to 1 million messages per month. Check out the [PubNub Swift SDK documentation](https://www.pubnub.com/docs/swift/pubnub-swift-sdk), or any of the other [75+ PubNub client SDKs](https://www.pubnub.com/developers/). +PubNub 提供每个月一百万条免费的消息。这里有 [PubNub Swift SDK 文档](https://www.pubnub.com/docs/swift/pubnub-swift-sdk),以及其他 [75+ PubNub 客户端 SDKs。](https://www.pubnub.com/developers/) > 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 From 1217bb4c0c81302166f94c9f26329e9f57a29277 Mon Sep 17 00:00:00 2001 From: LeviDing Date: Sat, 13 Jul 2019 13:35:30 +0800 Subject: [PATCH 32/68] Create the-10-statistical-techniques-data-scientists-need-to-master.md (#6126) * Create the-10-statistical-techniques-data-scientists-need-to-master.md * Update the-10-statistical-techniques-data-scientists-need-to-master.md --- ...chniques-data-scientists-need-to-master.md | 156 ++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 TODO1/the-10-statistical-techniques-data-scientists-need-to-master.md diff --git a/TODO1/the-10-statistical-techniques-data-scientists-need-to-master.md b/TODO1/the-10-statistical-techniques-data-scientists-need-to-master.md new file mode 100644 index 00000000000..1617b177cb9 --- /dev/null +++ b/TODO1/the-10-statistical-techniques-data-scientists-need-to-master.md @@ -0,0 +1,156 @@ +> * 原文地址:[The 10 Statistical Techniques Data Scientists Need to Master](https://medium.com/cracking-the-data-science-interview/the-10-statistical-techniques-data-scientists-need-to-master-1ef6dbd531f7) +> * 原文作者:[James Le](https://medium.com/@james_aka_yale) +> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) +> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/the-10-statistical-techniques-data-scientists-need-to-master.md](https://github.com/xitu/gold-miner/blob/master/TODO1/the-10-statistical-techniques-data-scientists-need-to-master.md) +> * 译者: +> * 校对者: + +# The 10 Statistical Techniques Data Scientists Need to Master + +![](https://cdn-images-1.medium.com/max/3840/1*itOusDBOUogAV1QbNaj4cQ.png) + +Regardless of where you stand on the matter of Data Science sexiness, it’s simply impossible to ignore the continuing importance of data, and our ability to analyze, organize, and contextualize it. Drawing on their vast stores of employment data and employee feedback, Glassdoor ranked Data Scientist #1 in their [25 Best Jobs in America](https://www.glassdoor.com/Best-Jobs-in-America-LST_KQ0,20.htm) list. So the role is here to stay, but unquestionably, the specifics of what a Data Scientist does will evolve. With technologies like Machine Learning becoming ever-more common place, and emerging fields like Deep Learning gaining significant traction amongst researchers and engineers — and the companies that hire them — Data Scientists continue to ride the crest of an incredible wave of innovation and technological progress. + +While having a strong coding ability is important, data science isn’t all about software engineering (in fact, have a good familiarity with Python and you’re good to go). Data scientists live at the intersection of coding, statistics, and critical thinking. [As Josh Wills](https://www.quora.com/What-is-the-difference-between-a-data-scientist-and-a-statistician) put it, **“data scientist is a person who is better at statistics than any programmer and better at programming than any statistician.”** I personally know too many software engineers looking to transition into data scientist and blindly utilizing machine learning frameworks such as TensorFlow or Apache Spark to their data without a thorough understanding of statistical theories behind them. So comes the study of [statistical learning](https://en.wikipedia.org/wiki/Statistical_learning_theory), a theoretical framework for machine learning drawing from the fields of statistics and functional analysis. + +**Why study Statistical Learning?** It is important to understand the ideas behind the various techniques, in order to know how and when to use them. One has to understand the simpler methods first, in order to grasp the more sophisticated ones. It is important to accurately assess the performance of a method, to know how well or how badly it is working. Additionally, this is an exciting research area, having important applications in science, industry, and finance. Ultimately, statistical learning is a fundamental ingredient in the training of a modern data scientist. Examples of Statistical Learning problems include: + +* Identify the risk factors for prostate cancer. +* Classify a recorded phoneme based on a log-periodogram. +* Predict whether someone will have a heart attack on the basis of demographic, diet and clinical measurements. +* Customize an email spam detection system. +* Identify the numbers in a handwritten zip code. +* Classify a tissue sample into one of several cancer classes. +* Establish the relationship between salary and demographic variables in population survey data. + +In my last semester in college, I did an Independent Study on Data Mining. The class covers expansive materials coming from 3 books: [Intro to Statistical Learning](http://www-bcf.usc.edu/~gareth/ISL/) (Hastie, Tibshirani, Witten, James), [Doing Bayesian Data Analysis](https://sites.google.com/site/doingbayesiandataanalysis/) (Kruschke), and [Time Series Analysis and Applications](http://www.stat.pitt.edu/stoffer/tsa4/) (Shumway, Stoffer). We did a lot of exercises on Bayesian Analysis, Markov Chain Monte Carlo, Hierarchical Modeling, Supervised and Unsupervised Learning. This experience deepens my interest in the Data Mining academic field and convinces me to specialize further in it. Recently, I completed the [Statistical Learning online course](https://lagunita.stanford.edu/courses/HumanitiesSciences/StatLearning/Winter2016/about) on Stanford Lagunita, which covers all the material in the [**Intro to Statistical Learning book**](https://www.amazon.com/Introduction-Statistical-Learning-Applications-Statistics/dp/1461471370) I read in my Independent Study. Now being exposed to the content twice, I want to share the 10 statistical techniques from the book that I believe any data scientists should learn to be more effective in handling big datasets. + +Before moving on with these 10 techniques, I want to differentiate between statistical learning and machine learning. I wrote [one of the most popular Medium posts on machine learning](https://gab41.lab41.org/the-10-algorithms-machine-learning-engineers-need-to-know-f4bb63f5b2fa) before, so I am confident I have the expertise to justify these differences: + +* Machine learning arose as a subfield of Artificial Intelligence. +* Statistical learning arose as a subfield of Statistics. +* Machine learning has a greater emphasis on large scale applications and prediction accuracy. +* Statistical learning emphasizes models and their interpretability, and precision and uncertainty. +* But the distinction has become and more blurred, and there is a great deal of “cross-fertilization.” +* Machine learning has the upper hand in Marketing! + +## 1 — Linear Regression: + +In statistics, linear regression is a method to predict a target variable by fitting the **best linear relationship** between the dependent and independent variable. The **best fit** is done by making sure that the sum of all the distances between the shape and the actual observations at each point is as small as possible. The fit of the shape is “best” in the sense that no other position would produce less error given the choice of shape. 2 major types of linear regression are **Simple Linear Regression** and **Multiple Linear Regression****. ****Simple Linear Regression **uses a single independent variable to predict a dependent variable by fitting a best linear relationship.** Multiple Linear Regression** uses more than one independent variable to predict a dependent variable by fitting a best linear relationship. + +![](https://cdn-images-1.medium.com/max/4328/1*KwdVLH5e_P9h8hEzeIPnTg.png) + +Pick any 2 things that you use in your daily life and that are related. Like, I have data of my monthly spending, monthly income and the number of trips per month for the last 3 years. Now I need to answer the following questions: + +* What will be my monthly spending for next year? +* Which factor (monthly income or number of trips per month) is more important in deciding my monthly spending? +* How monthly income and trips per month are correlated with monthly spending? + +## 2 — Classification: + +Classification is a data mining technique that assigns categories to a collection of data in order to aid in more accurate predictions and analysis. Also sometimes called a Decision Tree, classification is one of several methods intended to make the analysis of very large datasets effective. 2 major Classification techniques stand out: **Logistic Regression** and **Discriminant Analysis****.** + +**Logistic Regression** is the appropriate regression analysis to conduct when the dependent variable is dichotomous (binary). Like all regression analyses, the logistic regression is a predictive analysis. Logistic regression is used to describe data and to explain the relationship between one dependent binary variable and one or more nominal, ordinal, interval or ratio-level independent variables. Types of questions that a logistic regression can examine: + +* How does the probability of getting lung cancer (Yes vs No) change for every additional pound of overweight and for every pack of cigarettes smoked per day? +* Do body weight calorie intake, fat intake, and participant age have an influence on heart attacks (Yes vs No)? + +![](https://cdn-images-1.medium.com/max/2000/1*_jCbRluq1_g89LhNgIujLg.png) + +In **Discriminant Analysis**, 2 or more groups or clusters or populations are known a priori and 1 or more new observations are classified into 1 of the known populations based on the measured characteristics. Discriminant analysis models the distribution of the predictors X separately in each of the response classes, and then uses Bayes’ theorem to flip these around into estimates for the probability of the response category given the value of X. Such models can either be **linear** or **quadratic****.** + +* **Linear Discriminant Analysis** computes “discriminant scores” for each observation to classify what response variable class it is in. These scores are obtained by finding linear combinations of the independent variables. It assumes that the observations within each class are drawn from a multivariate Gaussian distribution and the covariance of the predictor variables are common across all k levels of the response variable Y. +* **Quadratic Discriminant Analysis** provides an alternative approach. Like LDA, QDA assumes that the observations from each class of Y are drawn from a Gaussian distribution. However, unlike LDA, QDA assumes that each class has its own covariance matrix. In other words, the predictor variables are not assumed to have common variance across each of the k levels in Y. + +## 3 — Resampling Methods: + +Resampling is the method that consists of drawing repeated samples from the original data samples. It is a non-parametric method of statistical inference. In other words, the method of resampling does not involve the utilization of the generic distribution tables in order to compute approximate p probability values. + +Resampling generates a unique sampling distribution on the basis of the actual data. It uses experimental methods, rather than analytical methods, to generate the unique sampling distribution. It yields unbiased estimates as it is based on the unbiased samples of all the possible results of the data studied by the researcher. In order to understand the concept of resampling, you should understand the terms **Bootstrapping** and **Cross-Validation**: + +![](https://cdn-images-1.medium.com/max/2000/1*SebBhTd29KMJ25JfPn2QgA.png) + +* **Bootstrapping** is a technique that helps in many situations like validation of a predictive model performance, ensemble methods, estimation of bias and variance of the model. It works by sampling with replacement from the original data, and take the “**not chosen**” data points as test cases. We can make this several times and calculate the average score as estimation of our model performance. +* On the other hand, **cross validation** is a technique for validating the model performance, and it’s done by split the training data into k parts. We take the k — 1 parts as our training set and use the “**held out**” part as our test set. We repeat that k times differently. Finally, we take the average of the k scores as our performance estimation. + +Usually for linear models, ordinary least squares is the major criteria to be considered to fit them into the data. The next 3 methods are the alternative approaches that can provide better prediction accuracy and model interpretability for fitting linear models. + +## 4 — Subset Selection: + +This approach identifies a subset of the **p** predictors that we believe to be related to the response. We then fit a model using the least squares of the subset features. + +![](https://cdn-images-1.medium.com/max/2000/1*R1tdVlwJX-N1qnFLzGp0pQ.png) + +* **Best-Subset Selection:** Here we fit a separate OLS regression for each possible combination of the **p** predictors and then look at the resulting model fits. The algorithm is broken up into 2 stages: (1) Fit all models that contain **k** predictors, where **k** is the max length of the models, (2) Select a single model using cross-validated prediction error. It is important to use **testing** or **validation error,** and not training error to assess model fit because RSS and R² monotonically increase with more variables. The best approach is to cross-validate and choose the model with the highest R² and lowest RSS on testing error estimates. +* **Forward Stepwise Selection** considers a much smaller subset of **p** predictors. It begins with a model containing no predictors, then adds predictors to the model, one at a time until all of the predictors are in the model. The order of the variables being added is the variable, which gives the greatest addition improvement to the fit, until no more variables improve model fit using cross-validated prediction error. +* **Backward Stepwise Selection** begins will all **p** predictors in the model, then iteratively removes the least useful predictor one at a time. +* **Hybrid Methods** follows the forward stepwise approach, however, after adding each new variable, the method may also remove variables that do not contribute to the model fit. + +## 5 — Shrinkage: + +This approach fits a model involving all **p** predictors, however, the estimated coefficients are shrunken towards zero relative to the least squares estimates. This shrinkage, aka **regularization** has the effect of reducing variance. Depending on what type of shrinkage is performed, some of the coefficients may be estimated to be exactly zero. Thus this method also performs variable selection. The two best-known techniques for shrinking the coefficient estimates towards zero are the **ridge regression** and the **lasso**. + +![](https://cdn-images-1.medium.com/max/2000/1*nlg3Mo5du17JV8VmLQ8H4g.jpeg) + +* **Ridge regression** is similar to least squares except that the coefficients are estimated by minimizing a slightly different quantity. Ridge regression, like OLS, seeks coefficient estimates that reduce RSS, however they also have a shrinkage penalty when the coefficients come closer to zero. This penalty has the effect of shrinking the coefficient estimates towards zero. Without going into the math, it is useful to know that ridge regression shrinks the features with the smallest column space variance. Like in prinicipal component analysis, ridge regression projects the data into **d**directional space and then shrinks the coefficients of the low-variance components more than the high variance components, which are equivalent to the largest and smallest principal components. +* Ridge regression had at least one disadvantage; it includes all **p** predictors in the final model. The penalty term will set many of them close to zero, but never **exactly** to zero. This isn’t generally a problem for prediction accuracy, but it can make the model more difficult to interpret the results. **Lasso** overcomes this disadvantage and is capable of forcing some of the coefficients to zero granted that **s** is small enough. Since **s** = 1 results in regular OLS regression, as **s** approaches 0 the coefficients shrink towards zero. Thus, Lasso regression also performs variable selection. + +## 6 — Dimension Reduction: + +Dimension reduction reduces the problem of estimating **p + 1** coefficients to the simple problem of **M + 1** coefficients, where **M \< p.** This is attained by computing **M** different **linear combinations,** or **projections,** of the variables. Then these **M** projections are used as predictors to fit a linear regression model by least squares. 2 approaches for this task are **principal component regression** and **partial least squares.** + +![](https://cdn-images-1.medium.com/max/2000/1*WVFe7w1rzZWsmghdvaoXag.png) + +* One can describe **Principal Components Regression** as an approach for deriving a low-dimensional set of features from a large set of variables. The **first** principal component direction of the data is along which the observations vary the most. In other words, the first PC is a line that fits as close as possible to the data. One can fit **p** distinct principal components. The second PC is a linear combination of the variables that is uncorrelated with the first PC, and has the largest variance subject to this constraint. The idea is that the principal components capture the most variance in the data using linear combinations of the data in subsequently orthogonal directions. In this way, we can also combine the effects of correlated variables to get more information out of the available data, whereas in regular least squares we would have to discard one of the correlated variables. +* The PCR method that we described above involves identifying linear combinations of **X** that best represent the predictors. These combinations (**directions**) are identified in an unsupervised way, since the response **Y** is not used to help determine the principal component directions. That is, the response **Y** does not **supervise** the identification of the principal components, thus there is no guarantee that the directions that best explain the predictors also are the best for predicting the response (even though that is often assumed). **Partial least square**s (PLS) are a **supervised**alternative to PCR. Like PCR, PLS is a dimension reduction method, which first identifies a new smaller set of features that are linear combinations of the original features, then fits a linear model via least squares to the new **M** features. Yet, unlike PCR, PLS makes use of the response variable in order to identify the new features. + +## 7 — Nonlinear Models: + +In statistics, nonlinear regression is a form of regression analysis in which observational data are modeled by a function which is a nonlinear combination of the model parameters and depends on one or more independent variables. The data are fitted by a method of successive approximations. Below are a couple of important techniques to deal with nonlinear models: + +* A function on the real numbers is called a **step function** if it can be written as a finite linear combination of indicator functions of intervals. Informally speaking, a step function is a piecewise constant function having only finitely many pieces. +* A **piecewise function** is a function which is defined by multiple sub-functions, each sub-function applying to a certain interval of the main function’s domain. Piecewise is actually a way of expressing the function, rather than a characteristic of the function itself, but with additional qualification, it can describe the nature of the function. For example, a **piecewise polynomial** function is a function that is a polynomial on each of its sub-domains, but possibly a different one on each. + +![](https://cdn-images-1.medium.com/max/2000/1*_vb4tu4Vvi8b2Rg7hzf5NQ.png) + +* A **spline** is a special function defined piecewise by polynomials. In computer graphics, spline refers to a piecewise polynomial parametric curve. Splines are popular curves because of the simplicity of their construction, their ease and accuracy of evaluation, and their capacity to approximate complex shapes through curve fitting and interactive curve design. +* A **generalized additive model** is a generalized linear model in which the linear predictor depends linearly on unknown smooth functions of some predictor variables, and interest focuses on inference about these smooth functions. + +## 8 — Tree-Based Methods: + +Tree-based methods can be used for both regression and classification problems. These involve stratifying or segmenting the predictor space into a number of simple regions. Since the set of splitting rules used to segment the predictor space can be summarized in a tree, these types of approaches are known as **decision-tree** methods. The methods below grow multiple trees which are then combined to yield a single consensus prediction. + +* **Bagging** is the way decrease the variance of your prediction by generating additional data for training from your original dataset using combinations with repetitions to produce multistep of the same carnality/size as your original data. By increasing the size of your training set you can’t improve the model predictive force, but just decrease the variance, narrowly tuning the prediction to expected outcome. +* **Boosting** is an approach to calculate the output using several different models and then average the result using a weighted average approach. By combining the advantages and pitfalls of these approaches by varying your weighting formula you can come up with a good predictive force for a wider range of input data, using different narrowly tuned models. + +![](https://cdn-images-1.medium.com/max/2000/1*W70TAcPDXVexTL6JNED6OA.png) + +* The **random forest** algorithm is actually very similar to bagging. Also here, you draw random bootstrap samples of your training set. However, in addition to the bootstrap samples, you also draw a random subset of features for training the individual trees; in bagging, you give each tree the full set of features. Due to the random feature selection, you make the trees more independent of each other compared to regular bagging, which often results in better predictive performance (due to better variance-bias trade-offs) and it’s also faster, because each tree learns only from a subset of features. + +## 9 — Support Vector Machines: + +![](https://cdn-images-1.medium.com/max/2000/1*MStS2dBWSZo8iJPiL2_uXg.png) + +SVM is a classification technique that is listed under supervised learning models in Machine Learning. In layman’s terms, it involves finding the hyperplane (line in 2D, plane in 3D and hyperplane in higher dimensions. More formally, a hyperplane is n-1 dimensional subspace of an n-dimensional space) that best separates two classes of points with the maximum margin. Essentially, it is a constrained optimization problem where the margin is maximized subject to the constraint that it perfectly classifies the data (hard margin). + +The data points that kind of “support” this hyperplane on either sides are called the “support vectors”. In the above picture, the filled blue circle and the two filled squares are the support vectors. For cases where the two classes of data are not linearly separable, the points are projected to an exploded (higher dimensional) space where linear separation may be possible. A problem involving multiple classes can be broken down into multiple one-versus-one or one-versus-rest binary classification problems. + +## 10 — Unsupervised Learning: + +So far, we only have discussed supervised learning techniques, in which the groups are known and the experience provided to the algorithm is the relationship between actual entities and the group they belong to. Another set of techniques can be used when the groups (categories) of data are not known. They are called unsupervised as it is left on the learning algorithm to figure out patterns in the data provided. Clustering is an example of unsupervised learning in which different data sets are clustered into groups of closely related items. Below is the list of most widely used unsupervised learning algorithms: + +![](https://cdn-images-1.medium.com/max/2000/1*DwqQu4oiGTsa5L--DD0v6Q.jpeg) + +* **Principal Component Analysis** helps in producing low dimensional representation of the dataset by identifying a set of linear combination of features which have maximum variance and are mutually un-correlated. This linear dimensionality technique could be helpful in understanding latent interaction between the variable in an unsupervised setting. +* **k-Means clustering**: partitions data into k distinct clusters based on distance to the centroid of a cluster. +* **Hierarchical clustering**: builds a multilevel hierarchy of clusters by creating a cluster tree. + +This was a basic run-down of some basic statistical techniques that can help a data science program manager and or executive have a better understanding of what is running underneath the hood of their data science teams. Truthfully, some data science teams purely run algorithms through python and R libraries. Most of them don’t even have to think about the math that is underlying. However, being able to understand the basics of statistical analysis gives your teams a better approach. Have insight into the smallest parts allows for easier manipulation and abstraction. I hope this basic data science statistical guide gives you a decent understanding! + +**P.S: You can get all the lecture slides and RStudio sessions from [my GitHub source code here](https://github.com/khanhnamle1994/statistical-learning). Thanks for the overwhelming response!** + +> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 + +--- + +> [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[区块链](https://github.com/xitu/gold-miner#区块链)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计)、[人工智能](https://github.com/xitu/gold-miner#人工智能)等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。 From bb1be4ebd12786ec56766baaf3e85c4b56d00039 Mon Sep 17 00:00:00 2001 From: LeviDing Date: Sat, 13 Jul 2019 13:35:42 +0800 Subject: [PATCH 33/68] Create 101-tips-for-being-a-great-programmer-human.md (#6128) * Create 101-tips-for-being-a-great-programmer-human.md * Update 101-tips-for-being-a-great-programmer-human.md --- ...tips-for-being-a-great-programmer-human.md | 460 ++++++++++++++++++ 1 file changed, 460 insertions(+) create mode 100644 TODO1/101-tips-for-being-a-great-programmer-human.md diff --git a/TODO1/101-tips-for-being-a-great-programmer-human.md b/TODO1/101-tips-for-being-a-great-programmer-human.md new file mode 100644 index 00000000000..b41e0564ad0 --- /dev/null +++ b/TODO1/101-tips-for-being-a-great-programmer-human.md @@ -0,0 +1,460 @@ +> * 原文地址:[101 Tips For Being A Great Programmer (& Human)](https://dev.to/emmawedekind/101-tips-for-being-a-great-programmer-human-36nl) +> * 原文作者:[Emma Wedekind](https://dev.to/emmawedekind) +> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) +> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/101-tips-for-being-a-great-programmer-human.md](https://github.com/xitu/gold-miner/blob/master/TODO1/101-tips-for-being-a-great-programmer-human.md) +> * 译者: +> * 校对者: + +# 101 Tips For Being A Great Programmer (& Human) + +## 1. Get good at Googling + +Being a programmer is all about learning how to search for the answers to your questions. By learning to Google things effectively, you'll save a lot of development time. + +## 2. Under promise and over deliver + +It's better to let your team know a task will take three weeks and deliver in two than the other way around. By under promising and over delivering, you'll build trust. + +[![Designer](https://res.cloudinary.com/practicaldev/image/fetch/s--XuAuxqWV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/umrbnosn8g68nep19y21.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--XuAuxqWV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/umrbnosn8g68nep19y21.png) + +## 3. Be nice to your designers; they're your friends + +Designers provide solutions to user pain points. Learn from them and work cohesively to build effective products. + +## 4. Find a mentor + +Find someone you can learn from and bounce ideas off of. [Coding Coach](https://codingcoach.io/) is a great place to get started if you need a technical mentor! + +## 5. Be a mentor + +Be someone others can learn from and bounce ideas off of. We'd love to have you as a mentor over at [Coding Coach](https://codingcoach.io/) + +## 6. Write useful comments + +Write comments which explain the "why" and not the "what". + +## 7. Name variables and functions appropriately + +Functions and variables should accurately denote their purpose, so `myCoolFunction` won't fly. + +## 8. Take vacations + +We all need time to de-compress. Take that trip you've been wanting. Your brain and your co-workers will thank you. + +## 9. Delete unused code + +No reason to accrue more technical debt. + +## 10. Learn to read code + +Reading code is an undervalued skill, but an invaluable one. + +## 11. Establish a healthy work/life balance + +You need time to de-compress after a long workday. Shut off work notifications, remove apps off your phone. + +[![Meeting](https://res.cloudinary.com/practicaldev/image/fetch/s--VqSUBCSo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/fv2vh91wlhe9dh046mub.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--VqSUBCSo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/fv2vh91wlhe9dh046mub.png) + +## 12. Only schedule necessary meetings + +Can it be solved in an email or a Slack message? If so, avoid a meeting. If not, be conscious of the duration. Aim for less. + +## 13. Pair program + +Pair programming allows you to play the role of both teacher and student. + +## 14. Write great emails + +Learn to capture your audience in your emails by being succinct yet clear. Nobody wants to read your four-page email Jerry. + +## 15. Get involved in the community + +Surrounding yourself with like-minded people will motivate you to push through the lows. + +[![Tree](https://res.cloudinary.com/practicaldev/image/fetch/s--T7zjbX2p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/gpvnfky8mmwakwy8eix1.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--T7zjbX2p--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/gpvnfky8mmwakwy8eix1.png) + +## 16. Clean up your branches + +Clean up your version control branches like you'd clean your house before your in-laws came for a visit. If you don't need it, discard it; don't just throw it in the closet. + +## 17. Don't gate keep + +Be inclusive. Don't tell others they aren't good enough to be in the industry. Everyone has value. + +## 18. Keep learning + +You've chosen a profession that requires continuous learning. Learn to love it. + +## 19. Don't give up + +It won't always be easy. But we all started at the same place. You can do it. + +## 20. Take tasks that scare you + +If it doesn't scare you, it isn't going to help you grow. + +## 21. Clarify requirements before starting + +You should understand the acceptance criteria before delving into writing the code. It will save you time and pain later down the line. + +[![React](https://res.cloudinary.com/practicaldev/image/fetch/s--LXs3CSyq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/iiagpq5fbypu8h9sggqn.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--LXs3CSyq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/iiagpq5fbypu8h9sggqn.png) + +## 22. Have a toolbox + +Have a set of tools which you know inside-and-out. Know which tools serve which purpose and when a project can benefit from using one over another. + +## 23. Learn to love constructive criticism + +Ask trusted colleagues and friends for constructive criticism. It will help you grow as a programmer and as a human. + +## 24. Be open-minded + +Technology changes, and it changes quickly. Don't oppose new technology; learn it and then form an opinion. + +## 25. Stay relevant + +Stay up-to-date on the latest tech news by following publications, blogs, podcasts, and tech news. + +## 26. Focus on problem solving + +Strong problem solving skills can conquer any problem. Hone in on what it takes to solve a problem. + +## 27. Stay humble + +No matter what title you hold or what company you work form, stay humble. + +[![Presentation](https://res.cloudinary.com/practicaldev/image/fetch/s--nhgfS-7z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/3skzkor6amr46291uvtp.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--nhgfS-7z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/3skzkor6amr46291uvtp.png) + +## 28. Learn to give a great presentation + +Learn how to captivate your audience and give effective presentations. + +## 29. Examine all solutions before jumping in + +Don't jump straight into the first possible solution. Examine all paths before delving into the code. + +## 30. Find your niche + +There are many divisions within the tech industry. Find the area that interests you most and become an expert. + +## 31. Develop good habits + +Try to build consistent, and healthy, habits such as removing distractions, time-boxing tasks, being present in meetings, and starting with the most important task first. It might take some getting used to but it will be worth it in the long-run. + +[![Debug](https://res.cloudinary.com/practicaldev/image/fetch/s--zD_K7d71--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/scn44lf4b9moiyp1teei.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--zD_K7d71--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/scn44lf4b9moiyp1teei.png) + +## 32. Learn to debug + +Explore the browser debugger tools. Learn the ins-and-outs of debugging with your IDE. By learning the most effective methods for debugging a problem and tracing errors, you'll be able to solve even the most difficult bugs. + +## 33. Exercise your current skills + +Just because you currently know a skill doesn't mean you shouldn't exercise it. Skills fade with time unless consciously improved upon, and this industry evolves so rapidly it's important to keep practicing. Get out of the mindset that "I've always done it this way" and into the mindset of "Is there a better way to do this?" + +Just because you've got a six pack now, doesn't mean you can eat a 🍩 a day and stay that way. + +## 34. Understand the why + +There will be times when you have to voice your opinion, so it's important to understand the why behind it. Why is solution A better than solution B? Provide a valid argument and your opinions will be much more sound. + +[![Money](https://res.cloudinary.com/practicaldev/image/fetch/s--3rau99gs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/bwftxcf9uigytg4bgfxm.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--3rau99gs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/bwftxcf9uigytg4bgfxm.png) + +## 35. Know your worth + +You are a commodity, and should be paid appropriately. Be aware of the industry averages in your geographic location. If you're making less money, it's time to have a chat with your manager. Go after what you deserve. + +## 36. Don't be afraid to ask for help + +If you're stuck on a problem and spending too much time searching for a solution, it's time to ask for help. We're all human. We all need help. There is no shame in reaching out to a colleague for support. + +## 37. Learn to learn + +People learn in different ways. Some learn best through video tutorials, others through reading a book. Figure out your learning style and practice it diligently. + +## 38. Be kind + +There will be times when you're asked to provide feedback on a colleague. Be kind. You can voice your opinions about Deborah's lack of initiative without ripping her to shreds. + +## 39. Take breaks + +It's nearly impossible to spend 8 consecutive hours coding. You'll burn out quickly and make a lot of mistakes. So set a timer to remind yourself to stop and take a break. Go for a walk. Get a coffee with a colleague. Stepping away from the screen will positively impact your productivity and the quality of your work. + +## 40. Track your progress + +Learning to code takes time and can be extremely disheartening when you don't see progress. So it's important to track your achievements and progress towards your goals. Keep a small list next to your computer and each time you achieve something, write it down, no matter how small. Atomic achievements compound to much larger rewards. + +[![React](https://res.cloudinary.com/practicaldev/image/fetch/s--97NnU31z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/iu5gfm3zns37g763quxt.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--97NnU31z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/iu5gfm3zns37g763quxt.png) + +## 41. Don't rely on a framework or library + +Learn the nuances of a language better than the ins-and-outs of a framework or library. You don't necessarily need to learn one before another, but understanding why a framework or library works the way it does will help you write cleaner and more performant code. + +## 42. Learn to love code reviews + +Having someone read and analyze your code can be terrifying, but can offer you invaluable feedback which will make you a better programmer. You should also work on your ability to conduct a good code review. + +## 43. Learn about tangential spaces + +Learn some basics about tangential spaces, such as design, marketing, frontend development or backend development. It will help you to become a more well-rounded programmer. + +## 44. Don't choose the comfortable technology; choose the right one + +Each project will have different needs, and as such we must choose the right tools for the job. Although it's comfortable to choose technologies you've worked with previously, if they don't suit the needs of the project, alternatives should be explored. + +## 45. Take responsibility for your mistakes + +All humans make mistakes and you will many many throughout your career. Thus it's important to own up and take responsibility when you've made a mistake. It will build trust with your team members and management. + +## 46. Review your own code + +Before opening a pull request, review your own code. If this were the work of a colleague, what comments would you make? It's important to first try to diagnose problems or mistakes before requesting a code review. + +## 47. Learn from your failures + +Failure is simply not achieving the expected outcome, and is not necessarily a bad thing. We all have many failures during the course of our careers. Learn from your downfalls. What can you do differently next time? + +## 48. Recognize your weaknesses + +Get to know yourself. What are your weaknesses? Maybe you always forget to update the tests before pushing. Or maybe you are really bad at replying to emails. Learn your weaknesses so you can actively work to address them. + +## 49. Stay curious + +This industry is ever-evolving, so curiosity will be important. If you don't understand something, be it a project requirement or a line of code, speak up. Nobody will criticize you for asking for clarification and you'll create better code as a result. + +[![Book](https://res.cloudinary.com/practicaldev/image/fetch/s--ypw1fzcI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/qzhic8yqs0pu28bo78lf.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--ypw1fzcI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/qzhic8yqs0pu28bo78lf.png) + +## 50. Don't try to learn everything + +There is an infinity pool of knowledge in the world and it is simply impossible to conquer it all. Pick several topics to master and leave the rest be. You can acquire working or tangential knowledge about other areas, but you cannot possibly master everything. + +## 51. Kill your darlings + +Just because you write some code doesn't mean you need t be emotionally attached to it. Nobody likes their work being thrown out, but code has a life cycle, so there's no need to be territorial about it. + +## 52. Have your team's back + +Good teams have each others' backs. This creates a safe space to try new things without fear of retribution. + +## 53. Find inspiration in the community + +Find a few people in the industry you admire. It will inspire you to keep working on your projects or try new things. + +## 54. Value your work + +Regardless of how much experience you have or what your job title is, your work has value. Give it the value it deserves. + +[![Phone](https://res.cloudinary.com/practicaldev/image/fetch/s--QyhOZjHL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/9gg3o6rl7retjl9n90ku.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--QyhOZjHL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/9gg3o6rl7retjl9n90ku.png) + +## 55. Disable distractions + +Turning off Slack notifications, text messages, emails, and social media will help you focus and maximize your workday.Jerry won't fall apart if it takes you 30 minutes to respond to his message. + +## 56. Be supportive + +Try and support your team members whether that's by attending an important presentation or helping them if they get stuck. + +## 57. Give credit where credit is due + +If someone does great work, tell them. Positive re-enforcement is a great way to build trust with your team members and help their careers. They'll be more likely to help you along as well. + +## 58. Test your code + +Tests are important. Unit tests, regression tests, integration tests, end-to-end tests. Test your code and your product will be much more stable. + +## 59. Plan out your approach + +When you receive a new feature request or get a new bug ticket, first plan your attack. What do you need to solve this problem or develop this feature? Taking even just a few minutes to plan your attack can save you hours of frustration. + +## 60. Learn to pseudocode + +Pseudocoding is a great skill to have because it allows you to think through complex problems without wasting time writing lines of code. Write an approach down on paper, run through different test cases and see where the pitfalls are. + +[![Win](https://res.cloudinary.com/practicaldev/image/fetch/s--uQ2UJkJY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/6l0lkgxbxz60sbsa3o59.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--uQ2UJkJY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/6l0lkgxbxz60sbsa3o59.png) + +## 61. Keep track of your achievements + +If you win an award at work, write it down. If you develop a crucial feature, write it down. You'll create a backlog of things which can aid with a promotion or boost your morale on a tough day. + +## 62. Learn programming foundations + +Learn some basic sorting and searching algorithms and data structures. These are language-agnostic and can help you solve problems across languages. + +## 63. Choose technology for longevity & maintainability + +Although it's fun to test out the newest technologies, pick those which will be easy to maintain within an enterprise application. Your team will thank you for years to come. + +## 64. Learn design patterns + +Design patterns are useful tools for architecting code. You may not need them for every project, but having a basic understanding of them will help scaffold out larger applications. + +## 65. Reduce ambiguity + +Instead of writing convoluted code which shows off your snazzy programming skills, aim for readability and simplicity. This will make it easier for your team members to contribute. + +[![Debt](https://res.cloudinary.com/practicaldev/image/fetch/s--fX3cKq9j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/6rg8pvj8ezhreshjjx6o.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--fX3cKq9j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/6rg8pvj8ezhreshjjx6o.png) + +## 66. Pay off technical debt + +Technical debt can have massive performance implications, so if you're able to refactor, you should. + +## 67. Ship often + +Instead of shipping a massive upgrade once every month, ship more frequently with smaller changelogs. You're less likely to introduce bugs and breaking changes. + +## 68. Commit early and often + +Committing early and committing often is the best way to ensure that your work remains clean and also reduces the stress of accidentally reverting important changes. + +## 69. Learn when to ask for help + +Not only should you not be afraid to ask for help, but you should learn when to ask for help. You should always try to solve a problem before asking for help, and keep track of the things you try. But when you've been stumped by a simple problem for over an hour, the cost outweighs the benefit, and you should reach out to a colleague. + +## 70. Ask effective questions + +When asking a question, try to be as specific as possible. + +## 71. Get feedback on unfinished work + +Your work doesn't need to be finished for you to get feedback. If you're uncertain of the direction, ask a trusted colleague to review the validity of your solution. + +[![Read](https://res.cloudinary.com/practicaldev/image/fetch/s--ajqLQ2p0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/pizzzb8twdan6231fdjq.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--ajqLQ2p0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/pizzzb8twdan6231fdjq.png) + +## 72. Read documentation + +Documentation is the purest source of truth about a technology, so learning to read it can quickly help you to become an expert. + +## 73. Try all the things + +Nothing is stopping you from trying a solution to a problem. What do you have to lose? + +## 74. Speak up in meetings + +Your ideas and opinions are valuable so participating in meetings will help you develop a rapport with your team as well as management. + +## 75. Collaborate cross-team + +If you get an opportunity to with with another team in your company, go for it. + +## 76. Have passion projects + +When you work 40 hours a week, it's important to take time for passion projects. They help you reinvigorate your love of programming and try new technologies you might not have access to at work. + +## 77. Define your career goals + +It's important to have an idea of your ideal trajectory for your career. If you don't, you're trying to shoot an arrow without having a target. + +[![Talk](https://res.cloudinary.com/practicaldev/image/fetch/s--SPitWbzG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/fjqq1ghzpz4qqcjhoew6.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--SPitWbzG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/fjqq1ghzpz4qqcjhoew6.png) + +## 78. Get involved in the conversation + +Comment on blogs, participate in Twitter threads. Engage with the community. You'll learn a lot more from being an active bystander than a wallflower. + +## 79. Prioritize tasks + +Learning to prioritize your tasks will help you enhance your productivity. Keep an active to-do list of immediate daily tasks as well as longer-term tasks and order them by most important. + +## 80. Don't overlook the details + +Details can make a big difference in a project. + +## 81. Trust your teammates + +Your teammates were hired for their skills. Use them and trust them to get the job done. + +## 82. Learn to delegate + +If you're in a leadership position, learn how to delegate effectively. It will save you time and frustration. You cannot do it all. + +## 83. Don't compare yourself to others + +The only thing you should compare yourself to is who you were yesterday. + +## 84. Surround yourself with allies + +Learning to program will be a long, and not always easy, journey. Surround yourself with the people who build you up and encourage you to keep going. + +[![Build](https://res.cloudinary.com/practicaldev/image/fetch/s---Cgcxmny--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/84lhizall1hn9flgvudv.png)](https://res.cloudinary.com/practicaldev/image/fetch/s---Cgcxmny--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/84lhizall1hn9flgvudv.png) + +## 85. Don't start for scale + +Starting for scale is a surefire way to become overwhelmed. Build with scalability in mind, but don't start scaling until you need it. This way you don't overwhelm your team with unnecessary bloat, but you maintain the ability to grow. + +## 86. Weigh performance implications + +If you want to use a cool, new technology you should weigh the performance implications of doing so. Could you implement something similar without taking a performance hit? If so, you may want to re-think your approach. + +## 87. Don't discriminate + +Don't discriminate against new technologies or ideas. Be open-minded about the possibility of learning new skills. Also don't discriminate against people. We all deserve respect. + +## 88. Apply for jobs you aren't qualified for + +You will never meet every requirement for a job. So take a chance and apply! What do you have to lose? + +## 89. Modularize your code + +You could write all of your code in one long file, but this isn't maintainable. By modularizing, we ensure that our code is easily digestible and testable. + +## 90. Don't JUST copy and paste + +If you're going to copy and paste a solution from Stack Overflow, you should understand **exactly** what it does. Be intentional about the code you choose to introduce. + +[![Coding](https://res.cloudinary.com/practicaldev/image/fetch/s--YyPmerE5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/uts4vd8ab10x8oct7fwb.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--YyPmerE5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/uts4vd8ab10x8oct7fwb.png) + +## 91. Create an inspiring environment/setup + +You'll be much more motivated to work if you enjoy your workspace and technical setup. Make it you. + +## 92. Remember where you came from + +We all started from the same place. As your skills and your job titles evolve, don't forget where you came from. + +## 93. Try to remain optimistic + +If something goes wrong, try and be optimistic. Tomorrow is a new day. Optimism will help your team dynamic and your mental health. + +## 94. Continually re-assess your workflow + +Just because something works now doesn't mean it always will. Re-evaluate your workflow and make adjustments where necessary. + +## 95. Learn how to work from home + +If you have the ability to work from home, learn to do so effectively. Find a separate office space, devoid of distractions. [Boneskull](https://dev.to/boneskull/pro-tips-for-devs-working-at-home-3b63) wrote a great article on working from home you should check out. + +[![Accessibility](https://res.cloudinary.com/practicaldev/image/fetch/s--Y-0GPXeE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/a45rv6rwqin3pzs3ztoo.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--Y-0GPXeE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/a45rv6rwqin3pzs3ztoo.png) + +## 96. Code for accessibility + +Accessibility isn't an afterthought, and it doesn't have to be difficult. Everyone should be able to use your products. + +## 97. Honor your commitments + +If you tell someone you'll deliver something by a certain date, honor that commitment. And if you can no longer make the deadline, speak up early. + +## 98. Be proactive + +If you have some extra bandwidth, find a task to help your team! They'll be thankful you were proactive. + +## 99. Build an amazing portfolio + +A great portfolio sets you apart from the crowd. Use this as a chance to show off your programming and design skills! + +## 100. Remember why you love programming + +You got into this profession because it sparked an interest. If you're getting frustrated and resentful, take a break. Give yourself space to reignite your passion for programming. + +## 101. Share your knowledge + +If you learn something cool, share it! Present at a local meetup or conference. Teach your coworker or mentee during lunch. Sharing your knowledge reinforces your knowledge while spreading the wealth. + +* * * + +[![Finished](https://res.cloudinary.com/practicaldev/image/fetch/s--pg0k20vY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/2hi4fky9ayd9dswlbdns.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--pg0k20vY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/2hi4fky9ayd9dswlbdns.png) + +And that's it! I hope you enjoyed my tips for being a great programmer (and human)! + +> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 + +--- + +> [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[区块链](https://github.com/xitu/gold-miner#区块链)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计)、[人工智能](https://github.com/xitu/gold-miner#人工智能)等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。 From 6279ab4e290c0766d3abd7cb311ca48ee01972c6 Mon Sep 17 00:00:00 2001 From: LeviDing Date: Sat, 13 Jul 2019 13:36:15 +0800 Subject: [PATCH 34/68] Create xgboost-algorithm-long-may-she-reign.md (#6127) * Create xgboost-algorithm-long-may-she-reign.md * Update xgboost-algorithm-long-may-she-reign.md --- TODO1/xgboost-algorithm-long-may-she-reign.md | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 TODO1/xgboost-algorithm-long-may-she-reign.md diff --git a/TODO1/xgboost-algorithm-long-may-she-reign.md b/TODO1/xgboost-algorithm-long-may-she-reign.md new file mode 100644 index 00000000000..4bc15c4e704 --- /dev/null +++ b/TODO1/xgboost-algorithm-long-may-she-reign.md @@ -0,0 +1,117 @@ +> * 原文地址:[XGBoost Algorithm: Long May She Reign!](https://towardsdatascience.com/https-medium-com-vishalmorde-xgboost-algorithm-long-she-may-rein-edd9f99be63d) +> * 原文作者:[Vishal Morde](https://medium.com/@vishalmorde) +> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) +> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/xgboost-algorithm-long-may-she-reign.md](https://github.com/xitu/gold-miner/blob/master/TODO1/xgboost-algorithm-long-may-she-reign.md) +> * 译者: +> * 校对者: + +# XGBoost Algorithm: Long May She Reign! + +![Photo by [Jared Subia](https://unsplash.com/photos/QczH4IiPNx0?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/search/photos/tiara?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)](https://cdn-images-1.medium.com/max/7260/1*kgJB2bz_asCdAsRd2pT0CA.png) + +> The new queen of Machine Learning algorithms taking over the world… + +(This article was co-authored with [Venkat Anurag Setty](https://towardsdatascience.com/u/e15e82916c90)) + +I still remember the day 1 of my very first job fifteen years ago. I had just finished my graduate studies and joined a global investment bank as an analyst. On my first day, I kept straightening my tie and trying to remember everything that I had studied. Meanwhile, deep down, I wondered if I was good enough for the corporate world. Sensing my anxiety, my boss smiled and said: + +**“Don’t worry! The only thing that you need to know is the regression modeling!”** + +I remember thinking myself, “I got this!”. I knew regression modeling; both linear and logistic regression. My boss was right. In my tenure, I exclusively built regression-based statistical models. I wasn’t alone. In fact, at that time, regression modeling was the undisputed queen of predictive analytics. Fast forward fifteen years, the era of regression modeling is over. The old queen has passed. Long live the new queen with a funky name; XGBoost or Extreme Gradient Boosting! + +*** + +## What is XGBoost? + +[XGBoost ](https://xgboost.ai/) is a decision-tree-based ensemble Machine Learning algorithm that uses a [gradient boosting](https://en.wikipedia.org/wiki/Gradient_boosting) framework. In prediction problems involving unstructured data (images, text, etc.) artificial neural networks tend to outperform all other algorithms or frameworks. However, when it comes to small-to-medium structured/tabular data, decision tree based algorithms are considered best-in-class right now. Please see the chart below for the evolution of tree-based algorithms over the years. + +![Evolution of XGBoost Algorithm from Decision Trees](https://cdn-images-1.medium.com/max/2000/1*QJZ6W-Pck_W7RlIDwUIN9Q.jpeg) + +XGBoost algorithm was developed as a research project at the University of Washington. [Tianqi Chen and Carlos Guestrin](https://arxiv.org/pdf/1603.02754.pdf) presented their paper at SIGKDD Conference in 2016 and caught the Machine Learning world by fire. Since its introduction, this algorithm has not only been credited with winning numerous Kaggle competitions but also for being the driving force under the hood for several cutting-edge industry applications. As a result, there is a strong community of data scientists contributing to the XGBoost open source projects with ~350 contributors and ~3,600 commits on [GitHub](https://github.com/dmlc/xgboost/). The algorithm differentiates itself in the following ways: + +1. A wide range of applications: Can be used to solve regression, classification, ranking, and user-defined prediction problems. +2. Portability: Runs smoothly on Windows, Linux, and OS X. +3. Languages: Supports all major programming languages including C++, Python, R, Java, Scala, and Julia. +4. Cloud Integration: Supports AWS, Azure, and Yarn clusters and works well with Flink, Spark, and other ecosystems. + +*** + +## How to build an intuition for XGBoost? + +Decision trees, in their simplest form, are easy-to-visualize and fairly interpretable algorithms but building intuition for the next-generation of tree-based algorithms can be a bit tricky. See below for a simple analogy to better understand the evolution of tree-based algorithms. + +![Photo by [rawpixel](https://unsplash.com/photos/cnseVhmbA7k?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/search/photos/interview?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)](https://cdn-images-1.medium.com/max/11030/1*Uwbv9Nzv7uoZV_hJwrsPGQ.jpeg) + +Imagine that you are a hiring manager interviewing several candidates with excellent qualifications. Each step of the evolution of tree-based algorithms can be viewed as a version of the interview process. + +1. **Decision Tree**: Every hiring manager has a set of criteria such as education level, number of years of experience, interview performance. A decision tree is analogous to a hiring manager interviewing candidates based on his or her own criteria. + +2. **Bagging**: Now imagine instead of a single interviewer, now there is an interview panel where each interviewer has a vote. Bagging or bootstrap aggregating involves combining inputs from all interviewers for the final decision through a democratic voting process. + +3. **Random Forest**: It is a bagging-based algorithm with a key difference wherein only a subset of features is selected at random. In other words, every interviewer will only test the interviewee on certain randomly selected qualifications (e.g. a technical interview for testing programming skills and a behavioral interview for evaluating non-technical skills). + +4. **Boosting**: This is an alternative approach where each interviewer alters the evaluation criteria based on feedback from the previous interviewer. This ‘boosts’ the efficiency of the interview process by deploying a more dynamic evaluation process. + +5. **Gradient Boosting**: A special case of boosting where errors are minimized by gradient descent algorithm e.g. the strategy consulting firms leverage by using case interviews to weed out less qualified candidates. + +6. **XGBoost**: Think of XGBoost as gradient boosting on ‘steroids’ (well it is called ‘Extreme Gradient Boosting’ for a reason!). It is a perfect combination of software and hardware optimization techniques to yield superior results using less computing resources in the shortest amount of time. + +*** + +## Why does XGBoost perform so well? + +XGBoost and Gradient Boosting Machines (GBMs) are both ensemble tree methods that apply the principle of boosting weak learners ([CARTs](https://www.datasciencecentral.com/profiles/blogs/introduction-to-classification-regression-trees-cart) generally) using the gradient descent architecture. However, XGBoost improves upon the base GBM framework through systems optimization and algorithmic enhancements. + +![How XGBoost optimizes standard GBM algorithm](https://cdn-images-1.medium.com/max/2000/1*FLshv-wVDfu-i54OqvZdHg.png) + +**System Optimization:** + +1. **Parallelization**: XGBoost approaches the process of sequential tree building using [parallelized](http://zhanpengfang.github.io/418home.html) implementation. This is possible due to the interchangeable nature of loops used for building base learners; the outer loop that enumerates the leaf nodes of a tree, and the second inner loop that calculates the features. This nesting of loops limits parallelization because without completing the inner loop (more computationally demanding of the two), the outer loop cannot be started. Therefore, to improve run time, the order of loops is interchanged using initialization through a global scan of all instances and sorting using parallel threads. This switch improves algorithmic performance by offsetting any parallelization overheads in computation. + +2. **Tree Pruning:** The stopping criterion for tree splitting within GBM framework is greedy in nature and depends on the negative loss criterion at the point of split. XGBoost uses ‘max_depth’ parameter as specified instead of criterion first, and starts pruning trees backward. This ‘depth-first’ approach improves computational performance significantly. + +3. **Hardware Optimization**: This algorithm has been designed to make efficient use of hardware resources. This is accomplished by cache awareness by allocating internal buffers in each thread to store gradient statistics. Further enhancements such as ‘out-of-core’ computing optimize available disk space while handling big data-frames that do not fit into memory. + +**Algorithmic Enhancements:** + +1. **Regularization**: It penalizes more complex models through both LASSO (L1) and Ridge (L2) [regularization](https://towardsdatascience.com/l1-and-l2-regularization-methods-ce25e7fc831c) to prevent overfitting. + +2. **Sparsity Awareness**: XGBoost naturally admits sparse features for inputs by automatically ‘learning’ best missing value depending on training loss and handles different types of [sparsity patterns](https://www.kdnuggets.com/2017/10/xgboost-concise-technical-overview.html) in the data more efficiently. + +3. **Weighted Quantile Sketch:** XGBoost employs the distributed [weighted Quantile Sketch algorithm](https://arxiv.org/pdf/1603.02754.pdf) to effectively find the optimal split points among weighted datasets. + +4. **Cross-validation**: The algorithm comes with built-in [cross-validation](https://towardsdatascience.com/cross-validation-in-machine-learning-72924a69872f) method at each iteration, taking away the need to explicitly program this search and to specify the exact number of boosting iterations required in a single run. + +*** + +## Where is the proof? + +We used Scikit-learn’s ‘[Make_Classification](https://scikit-learn.org/stable/modules/generated/sklearn.datasets.make_classification.html)’ data package to create a random sample of 1 million data points with 20 features (2 informative and 2 redundant). We tested several algorithms such as Logistic Regression, Random Forest, standard Gradient Boosting, and XGBoost. + +![XGBoost vs. Other ML Algorithms using SKLearn’s Make_Classification Dataset](https://cdn-images-1.medium.com/max/2000/1*U72CpSTnJ-XTjCisJqCqLg.jpeg) + +As demonstrated in the chart above, XGBoost model has the best combination of prediction performance and processing time compared to other algorithms. Other rigorous [benchmarking](https://github.com/szilard/benchm-ml) studies have produced similar results. No wonder XGBoost is widely used in recent Data Science competitions. + +> “When in doubt, use XGBoost” — Owen Zhang, Winner of [Avito](http://blog.kaggle.com/2015/08/26/avito-winners-interview-1st-place-owen-zhang/) Context Ad Click Prediction competition on Kaggle + +*** + +## So should we use just XGBoost all the time? + +When it comes to Machine Learning (or even life for that matter), there is no free lunch. As Data Scientists, we must test all possible algorithms for data at hand to identify the champion algorithm. Besides, picking the right algorithm is not enough. We must also choose the right configuration of the algorithm for a dataset by tuning the [hyper-parameters](https://www.analyticsvidhya.com/blog/2016/03/complete-guide-parameter-tuning-xgboost-with-codes-python/). Furthermore, there are several other considerations for choosing the winning algorithm such as computational complexity, explainability, and ease of implementation. This is exactly the point where Machine Learning starts drifting away from science towards art, but honestly, that’s where the magic happens! + +*** + +## What does the future hold? + +Machine Learning is a very active research area and already there are several viable alternatives to XGBoost. Microsoft Research recently released [LightGBM](https://www.microsoft.com/en-us/research/project/lightgbm/) framework for gradient boosting that shows great potential. [CatBoost](https://catboost.ai/) developed by Yandex Technology has been delivering impressive bench-marking results. It is a matter of time when we have a better model framework that beats XGBoost in terms of prediction performance, flexibility, explanability, and pragmatism. However, until a time when a strong challenger comes along, XGBoost will continue to reign over the Machine Learning world! + +*** + +Please leave me your comments below. Many thanks to [Venkat Anurag Setty](undefined) for co-authoring this article with me. + +> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 + +--- + +> [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[区块链](https://github.com/xitu/gold-miner#区块链)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计)、[人工智能](https://github.com/xitu/gold-miner#人工智能)等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。 From ef1f063e412d3aea6f5d0ee03c08a4fef505cf17 Mon Sep 17 00:00:00 2001 From: LeviDing Date: Sat, 13 Jul 2019 13:38:49 +0800 Subject: [PATCH 35/68] Update the-10-statistical-techniques-data-scientists-need-to-master.md --- ...-10-statistical-techniques-data-scientists-need-to-master.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO1/the-10-statistical-techniques-data-scientists-need-to-master.md b/TODO1/the-10-statistical-techniques-data-scientists-need-to-master.md index 1617b177cb9..067774a2f0d 100644 --- a/TODO1/the-10-statistical-techniques-data-scientists-need-to-master.md +++ b/TODO1/the-10-statistical-techniques-data-scientists-need-to-master.md @@ -36,7 +36,7 @@ Before moving on with these 10 techniques, I want to differentiate between stati ## 1 — Linear Regression: -In statistics, linear regression is a method to predict a target variable by fitting the **best linear relationship** between the dependent and independent variable. The **best fit** is done by making sure that the sum of all the distances between the shape and the actual observations at each point is as small as possible. The fit of the shape is “best” in the sense that no other position would produce less error given the choice of shape. 2 major types of linear regression are **Simple Linear Regression** and **Multiple Linear Regression****. ****Simple Linear Regression **uses a single independent variable to predict a dependent variable by fitting a best linear relationship.** Multiple Linear Regression** uses more than one independent variable to predict a dependent variable by fitting a best linear relationship. +In statistics, linear regression is a method to predict a target variable by fitting the **best linear relationship** between the dependent and independent variable. The **best fit** is done by making sure that the sum of all the distances between the shape and the actual observations at each point is as small as possible. The fit of the shape is “best” in the sense that no other position would produce less error given the choice of shape. 2 major types of linear regression are **Simple Linear Regression** and **Multiple Linear Regression**. **Simple Linear Regression **uses a single independent variable to predict a dependent variable by fitting a best linear relationship.** Multiple Linear Regression** uses more than one independent variable to predict a dependent variable by fitting a best linear relationship. ![](https://cdn-images-1.medium.com/max/4328/1*KwdVLH5e_P9h8hEzeIPnTg.png) From eb4cb43b7b7cb4df57660de352e3e75706bf7cf7 Mon Sep 17 00:00:00 2001 From: LeviDing Date: Sat, 13 Jul 2019 13:43:40 +0800 Subject: [PATCH 36/68] Create a-step-by-step-explanation-of-principal-component-analysis.md (#6129) * Create a-step-by-step-explanation-of-principal-component-analysis.md * Update a-step-by-step-explanation-of-principal-component-analysis.md --- ...anation-of-principal-component-analysis.md | 149 ++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 TODO1/a-step-by-step-explanation-of-principal-component-analysis.md diff --git a/TODO1/a-step-by-step-explanation-of-principal-component-analysis.md b/TODO1/a-step-by-step-explanation-of-principal-component-analysis.md new file mode 100644 index 00000000000..24570e92cba --- /dev/null +++ b/TODO1/a-step-by-step-explanation-of-principal-component-analysis.md @@ -0,0 +1,149 @@ +> * 原文地址:[A step by step explanation of Principal Component Analysis](https://towardsdatascience.com/a-step-by-step-explanation-of-principal-component-analysis-b836fb9c97e2) +> * 原文作者:[Zakaria Jaadi](https://medium.com/@zakaria.jaadi) +> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) +> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/a-step-by-step-explanation-of-principal-component-analysis.md](https://github.com/xitu/gold-miner/blob/master/TODO1/a-step-by-step-explanation-of-principal-component-analysis.md) +> * 译者: +> * 校对者: + +# A step by step explanation of Principal Component Analysis + +![](https://cdn-images-1.medium.com/max/2360/0*MCObvpuCqWS5-z2m) + +The purpose of this post is to provide a complete and simplified explanation of Principal Component Analysis, and especially to answer how it works step by step, so that everyone can understand it and make use of it, without necessarily having a strong mathematical background. + +PCA is actually a widely covered method on the web, and there are some great articles about it, but only few of them go straight to the point and explain how it works without diving too much into the technicalities and the ‘why’ of things. That’s the reason why i decided to make my own post to present it in a simplified way. + +Before getting to the explanation, this post provides logical explanations of what PCA is doing in each step and simplifies the mathematical concepts behind it, as standardization, covariance, eigenvectors and eigenvalues without focusing on how to compute them. + +## So what is Principal Component Analysis ? + +Principal Component Analysis, or PCA, is a dimensionality-reduction method that is often used to reduce the dimensionality of large data sets, by transforming a large set of variables into a smaller one that still contains most of the information in the large set. + +Reducing the number of variables of a data set naturally comes at the expense of accuracy, but the trick in dimensionality reduction is to trade a little accuracy for simplicity. Because smaller data sets are easier to explore and visualize and make analyzing data much easier and faster for machine learning algorithms without extraneous variables to process. + +So to sum up, the idea of PCA is simple — reduce the number of variables of a data set, while preserving as much information as possible. + +## Step by step explanation + +### Step 1: Standardization + +The aim of this step is to standardize the range of the continuous initial variables so that each one of them contributes equally to the analysis. + +More specifically, the reason why it is critical to perform standardization prior to PCA, is that the latter is quite sensitive regarding the variances of the initial variables. That is, if there are large differences between the ranges of initial variables, those variables with larger ranges will dominate over those with small ranges (For example, a variable that ranges between 0 and 100 will dominate over a variable that ranges between 0 and 1), which will lead to biased results. So, transforming the data to comparable scales can prevent this problem. + +Mathematically, this can be done by subtracting the mean and dividing by the standard deviation for each value of each variable. + +![](https://cdn-images-1.medium.com/max/2000/0*AgmY9auxftS9BI73.png) + +Once the standardization is done, all the variables will be transformed to the same scale. + +*** + +if you want to get an in-depth understanding about standardization, i invite you to read this simple article i wrote about it. + +* [**When and why to standardize your data ? A simple guide on when to standardize your data and when not to.**](https://github.com/xitu/gold-miner/blob/master/TODO1/when-to-standardize-your-data.md) + +### Step 2: Covariance Matrix computation + +The aim of this step is to understand how the variables of the input data set are varying from the mean with respect to each other, or in other words, to see if there is any relationship between them. Because sometimes, variables are highly correlated in such a way that they contain redundant information. So, in order to identify these correlations, we compute the covariance matrix. + +The covariance matrix is a **p** × **p**** **symmetric matrix (where** p **is the number of dimensions) that has as entries the covariances associated with all possible pairs of the initial variables. For example, for a 3-dimensional data set with 3 variables** x**,** y**, and** z**, the covariance matrix is a 3×3 matrix of this from: + +![Covariance matrix for 3-dimensional data](https://cdn-images-1.medium.com/max/2000/0*xTLQtW2XQY6P3mZf.png) + +Since the covariance of a variable with itself is its variance (Cov(a,a)=Var(a)), in the main diagonal (Top left to bottom right) we actually have the variances of each initial variable. And since the covariance is commutative (Cov(a,b)=Cov(b,a)), the entries of the covariance matrix are symmetric with respect to the main diagonal, which means that the upper and the lower triangular portions are equal. + +**What do the covariances that we have as entries of the matrix tell us about the correlations between the variables?** + +It’s actually the sign of the covariance that matters : + +* if positive then : the two variables increase or decrease together (correlated) +* if negative then : One increases when the other decreases (Inversely correlated) + +Now, that we know that the covariance matrix is not more than a table that summaries the correlations between all the possible pairs of variables, let’s move to the next step. + +### Step 3: Compute the eigenvectors and eigenvalues of the covariance matrix to identify the principal components + +Eigenvectors and eigenvalues are the linear algebra concepts that we need to compute from the covariance matrix in order to determine the **principal components** of the data. Before getting to the explanation of these concepts, let’s first understand what do we mean by principal components. + +Principal components are new variables that are constructed as linear combinations or mixtures of the initial variables. These combinations are done in such a way that the new variables (i.e., principal components) are uncorrelated and most of the information within the initial variables is squeezed or compressed into the first components. So, the idea is 10-dimensional data gives you 10 principal components, but PCA tries to put maximum possible information in the first component, then maximum remaining information in the second and so on, until having something like shown in the scree plot below. + +![Percentage of variance (information) for by each PC](https://cdn-images-1.medium.com/max/2304/1*JLAVaWW5609YZoJ-NYkSOA.png) + +Organizing information in principal components this way, will allow you to reduce dimensionality without losing much information, and this by discarding the components with low information and considering the remaining components as your new variables. + +An important thing to realize here is that, the principal components are less interpretable and don’t have any real meaning since they are constructed as linear combinations of the initial variables. + +Geometrically speaking, principal components represent the directions of the data that explain a **maximal amount of variance**, that is to say, the lines that capture most information of the data. The relationship between variance and information here, is that, the larger the variance carried by a line, the larger the dispersion of the data points along it, and the larger the dispersion along a line, the more the information it has. To put all this simply, just think of principal components as new axes that provide the best angle to see and evaluate the data, so that the differences between the observations are better visible. + +### How PCA constructs the Principal Components? + +As there are as many principal components as there are variables in the data, principal components are constructed in such a manner that the first principal component accounts for the **largest possible variance** in the data set. For example, let’s assume that the scatter plot of our data set is as shown below, can we guess the first principal component ? Yes, it’s approximately the line that matches the purple marks because it goes through the origin and it’s the line in which the projection of the points (red dots) is the most spread out. Or mathematically speaking, it’s the line that maximizes the variance (the average of the squared distances from the projected points (red dots) to the origin). + +![](https://cdn-images-1.medium.com/max/2000/1*UpFltkN-kT9aGqfLhOR9xg.gif) + +The second principal component is calculated in the same way, with the condition that it is uncorrelated with (i.e., perpendicular to) the first principal component and that it accounts for the next highest variance. + +This continues until a total of p principal components have been calculated, equal to the original number of variables. + +Now that we understood what we mean by principal components, let’s go back to eigenvectors and eigenvalues. What you firstly need to know about them is that they always come in pairs, so that every eigenvector has an eigenvalue. And their number is equal to the number of dimensions of the data. For example, for a 3-dimensional data set, there are 3 variables, therefore there are 3 eigenvectors with 3 corresponding eigenvalues. + +Without further ado, it is eigenvectors and eigenvalues who are behind all the magic explained above, because the eigenvectors of the Covariance matrix are actually **the** **directions of the axes where there is the most variance** (most information) and that we call Principal Components. And eigenvalues are simply the coefficients attached to eigenvectors, which give the **amount of variance carried in each Principal Component**. + +By ranking your eigenvectors in order of their eigenvalues, highest to lowest, you get the principal components in order of significance. + +**Example:** + +let’s suppose that our data set is 2-dimensional with 2 variables **x,y** and that the eigenvectors and eigenvalues of the covariance matrix are as follows: + +![](https://cdn-images-1.medium.com/max/2000/1*3OAdlot1vJcK6qzCePlq9Q.png) + +If we rank the eigenvalues in descending order, we get λ1>λ2, which means that the eigenvector that corresponds to the first principal component (PC1) is **v1** and the one that corresponds to the second component (PC2) is **v2.** + +After having the principal components, to compute the percentage of variance (information) accounted for by each component, we divide the eigenvalue of each component by the sum of eigenvalues. If we apply this on the example above, we find that PC1 and PC2 carry respectively 96% and 4% of the variance of the data. + +### Step 4: Feature vector + +As we saw in the previous step, computing the eigenvectors and ordering them by their eigenvalues in descending order, allow us to find the principal components in order of significance. In this step, what we do is, to choose whether to keep all these components or discard those of lesser significance (of low eigenvalues), and form with the remaining ones a matrix of vectors that we call **Feature vector**. + +So, the feature vector is simply a matrix that has as columns the eigenvectors of the components that we decide to keep. This makes it the first step towards dimensionality reduction, because if we choose to keep only **p** eigenvectors (components) out of **n**, the final data set will have only **p** dimensions. + +**Example**: + +Continuing with the example from the previous step, we can either form a feature vector with both of the eigenvectors **v**1 and **v**2: + +![](https://cdn-images-1.medium.com/max/2000/0*DwiYbyXZXvU20DjB.png) + +Or discard the eigenvector **v**2, which is the one of lesser significance, and form a feature vector with **v**1 only: + +![](https://cdn-images-1.medium.com/max/2000/0*YKNYKGQaNAYf6Iln.png) + +Discarding the eigenvector **v2** will reduce dimensionality by 1, and will consequently cause a loss of information in the final data set. But given that **v**2 was carrying only 4% of the information, the loss will be therefore not important and we will still have 96% of the information that is carried by **v**1. + +*** + +So, as we saw in the example, it’s up to you to choose whether to keep all the components or discard the ones of lesser significance, depending on what you are looking for. Because if you just want to describe your data in terms of new variables (principal components) that are uncorrelated without seeking to reduce dimensionality, leaving out lesser significant components is not needed. + +### Last step : Recast the data along the principal components axes + +In the previous steps, apart from standardization, you do not make any changes on the data, you just select the principal components and form the feature vector, but the input data set remains always in terms of the original axes (i.e, in terms of the initial variables). + +In this step, which is the last one, the aim is to use the feature vector formed using the eigenvectors of the covariance matrix, to reorient the data from the original axes to the ones represented by the principal components (hence the name Principal Components Analysis). This can be done by multiplying the transpose of the original data set by the transpose of the feature vector. + +![](https://cdn-images-1.medium.com/max/2000/0*D02r0HjB8WtCq3Cj.png) + +*** + +If you enjoyed this story, please click the 👏 button as many times as you think it deserves. And share to help others find it! Feel free to leave a comment below. + +### References: + +* [**Steven M. Holland**, **Univ. of Georgia**]: Principal Components Analysis +* [**skymind.ai**]: Eigenvectors, Eigenvalues, PCA, Covariance and Entropy +* [**Lindsay I. Smith**] : A tutorial on Principal Component Analysis + +> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 + +--- + +> [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[区块链](https://github.com/xitu/gold-miner#区块链)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计)、[人工智能](https://github.com/xitu/gold-miner#人工智能)等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。 From 00a04c593e227028f867642399ab99b108bf331d Mon Sep 17 00:00:00 2001 From: LeviDing Date: Sat, 13 Jul 2019 13:44:55 +0800 Subject: [PATCH 37/68] Create when-to-standardize-your-data.md (#6130) * Create when-to-standardize-your-data.md * Update when-to-standardize-your-data.md * Update when-to-standardize-your-data.md --- TODO1/when-to-standardize-your-data.md | 93 ++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 TODO1/when-to-standardize-your-data.md diff --git a/TODO1/when-to-standardize-your-data.md b/TODO1/when-to-standardize-your-data.md new file mode 100644 index 00000000000..95f6016d74e --- /dev/null +++ b/TODO1/when-to-standardize-your-data.md @@ -0,0 +1,93 @@ +> * 原文地址:[When and why to standardize your data ?](https://towardsdatascience.com/when-to-standardize-your-data-in-4-minutes-f9282190707e) +> * 原文作者:[Zakaria Jaadi](https://medium.com/@zakaria.jaadi) +> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) +> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/when-to-standardize-your-data.md](https://github.com/xitu/gold-miner/blob/master/TODO1/when-to-standardize-your-data.md) +> * 译者: +> * 校对者: + +# When and why to standardize your data? + +> A simple guide on when to standardize your data and when not to. + +![Credits : 365datascience.com](https://cdn-images-1.medium.com/max/NaN/1*dZlwWGNhFco5bmpfwYyLCQ.png) + +Standardization is an important technique that is generally performed as a pre-processing step before many Machine Learning models, to standardize the range of features of input data set. + +Some ML developers tend to standardize their data blindly before “every” Machine Learning model without taking the effort to understand why it must be used, or even if it’s needed or not. So the goal of this post is to explain how, why and when to standardize data. + +## Standardization + +Standardization comes into picture when features of input data set have large differences between their ranges, or simply when they are measured in different measurement units (e.g., Pounds, Meters, Miles … etc). + +These differences in the ranges of initial features causes trouble to many machine learning models. For example, for the models that are based on distance computation, if one of the features has a broad range of values, the distance will be governed by this particular feature. + +To illustrate this with an example: say we have a 2-dimensional data set with two features, Height in Meters and Weight in Pounds, that range respectively from [1 to 2] Meters and [10 to 200] Pounds. No matter what distance based model you perform on this data set, the Weight feature will dominate over the Height feature and will have more contribution to the distance computation, just because it has bigger values compared to the Height. So, to prevent this problem, transforming features to comparable scales using standardization is the solution. + +## How to standardize data? + +### Z-score + +Z-score is one of the most popular methods to standardize data, and can be done by subtracting the mean and dividing by the standard deviation for each value of each feature. + +![](https://cdn-images-1.medium.com/max/NaN/0*AgmY9auxftS9BI73.png) + +Once the standardization is done, all the features will have a mean of zero, a standard deviation of one, and thus, the same scale. + +> There exist other standardization methods but for the sake of simplicity, in this story i settle for Z-score method. + +## When to standardize data and why? + +As seen above, for distance based models, standardization is performed to prevent features with wider ranges from dominating the distance metric. But the reason we standardize data is not the same for all machine learning models, and differs from one model to another. + +So before which ML models and methods you have to standardize your data and why? + +**1- Before PCA:** + +In Principal Component Analysis, features with high variances/wide ranges, get more weight than those with low variance, and consequently, they end up illegitimately dominating the First Principal Components (Components with maximum variance). I used the word “Illegitimately” here, because the reason these features have high variances compared to the other ones is just because they were measured in different scales. + +Standardization can prevent this, by giving same wheightage to all features. + +**2- Before Clustering:** + +Clustering models are distance based algorithms, in order to measure similarities between observations and form clusters they use a distance metric. So, features with high ranges will have a bigger influence on the clustering. Therefore, standardization is required before building a clustering model. + +**3- Before KNN:** + +k-nearest neighbors is a distance based classifier that classifies new observations based on similarity measures (e.g., distance metrics) with labeled observations of the training set. Standardization makes all variables to contribute equally to the similarity measures . + +**4- Before SVM** + +Support Vector Machine tries to maximize the distance between the separating plane and the support vectors. If one feature has very large values, it will dominate over other features when calculating the distance. So Standardization gives all features the same influence on the distance metric. + +![Credits : Arun Manglick ([arun-aiml.blogspot.com](http://arun-aiml.blogspot.com/))](https://cdn-images-1.medium.com/max/2000/0*_taflmQxrsa0vguT.PNG) + +**5- Before measuring variable importance in regression models** + +You can measure variable importance in regression analysis, by fitting a regression model using the **standardized** independent variables and comparing the absolute value of their standardized coefficients. But, if the independent variables are not standardized, comparing their coefficients becomes meaningless. + +**6- Before Lasso and Ridge Regression** + +LASSO and Ridge regressions place a penalty on the magnitude of the coefficients associated to each variable. And the scale of variables will affect how much penalty will be applied on their coefficients. Because coefficients of variables with large variance are small and thus less penalized. Therefore, standardization is required before fitting both regressions. + +## Cases when standardization is not needed? + +**Logistic Regression and Tree based models** + +Logistic Regression and Tree based algorithms such as Decision Tree, Random forest and gradient boosting, are not sensitive to the magnitude of variables. So standardization is not needed before fitting this kind of models. + +## Conclusion + +As we saw in this post, when to standardize and when not to, depends on which model you want to use and what you want to do with it. So, it’s very important for a ML developer to understand the internal functioning of machine learning algorithms, to be able to know when to standardize data and to build a successful machine learning model. + +> N.B: The list of models and methods when standardization is required, presented in this post is not exhaustive. + +### References: + +* [**365DataScience.com**]: Explaining Standardization Step-By-Step +* [**Listendata.com** ]: when and why to standardize a variable + +> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 + +--- + +> [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[区块链](https://github.com/xitu/gold-miner#区块链)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计)、[人工智能](https://github.com/xitu/gold-miner#人工智能)等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。 From bec61ea08a12de4f83389e45ddb044fe1c38cb47 Mon Sep 17 00:00:00 2001 From: Jarvis <448300947@qq.com> Date: Sun, 14 Jul 2019 13:38:00 +0800 Subject: [PATCH 38/68] =?UTF-8?q?=E5=88=A9=E7=94=A8=2084=20=E7=A7=8D?= =?UTF-8?q?=E8=AE=A4=E7=9F=A5=E5=81=8F=E8=A7=81=E8=AE=BE=E8=AE=A1=E6=9B=B4?= =?UTF-8?q?=E5=A5=BD=E7=9A=84=E4=BA=A7=E5=93=81=20=E2=80=94=E2=80=94=20?= =?UTF-8?q?=E7=AC=AC=E4=B8=80=E9=83=A8=E5=88=86=20(#6114)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs(*): long opening and Part 1 * docs(*): 5 & 6 biases * docs(*): 7~10 biases * docs(*): Don’t call me a loser * docs(*): Disproportionately emotional * fix(*): Proofreading * fix(*): Proofreading * docs(*): Adding proofreader * Update collection-cognitive-biases-how-to-use-1.md --- ...ollection-cognitive-biases-how-to-use-1.md | 272 +++++++++--------- 1 file changed, 136 insertions(+), 136 deletions(-) diff --git a/TODO1/collection-cognitive-biases-how-to-use-1.md b/TODO1/collection-cognitive-biases-how-to-use-1.md index 9526cbb87e4..92564bfcb85 100644 --- a/TODO1/collection-cognitive-biases-how-to-use-1.md +++ b/TODO1/collection-cognitive-biases-how-to-use-1.md @@ -2,280 +2,280 @@ > * 原文作者:[@gilbouhnick](https://twitter.com/GilBouhnick) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/collection-cognitive-biases-how-to-use-1.md](https://github.com/xitu/gold-miner/blob/master/TODO1/collection-cognitive-biases-how-to-use-1.md) -> * 译者: -> * 校对者: +> * 译者:[江五渣](http://jalan.space) +> * 校对者:[mymmon](https://github.com/mymmon),[shinichi4849](https://github.com/shinichi4849) -# 84 cognitive biases you should exploit to design better products - Part 1 +# 利用 84 种认知偏见设计更好的产品 —— 第一部分 ![](https://2.bp.blogspot.com/-JvOvFjdlVfE/XMhvVVa0R4I/AAAAAAAAPrM/KaVBcSKDdPgb1PLug4TlVOx07uY6YHShQCLcBGAs/s640/Cognitive%2Bbiases.png) -This one is probably my longest post in my 15 years of blogging, and the result of occasional writing I've been doing in the past few months. +这可能是 15 年来我博客中最长的文章,也是我过去几个月偶然创作的结果。 -**Cognitive biases** are systematic errors in our thinking process that affect our decisions making. +**认知偏见**是我们思维过程中的系统性错误,它会影响我们的决策。 -As humans, we don’t always see things as they really are, or remember things as they really were. As a result, we create our own subjective social reality that affects our judgment. +作为人类,我们并不总是能看到或记住事物的本来面目。因此,我们创造了自己的主观社会现实,这影响了我们的判断。 -> As **product people**, we **should** to take advantage of these biases to create better products. +> 作为**产品人**,我们**应该**利用这些偏见来创造更好的产品。 -Not in a bad way of course, but in a way that will allow us to get a fair chance to prove that our products are worthy. Products can exploit common cognitive biases to establish trust with the users, improve conversion rates, increase their users’ engagement level, and as a result, improve retention rates. +这当然不是一件坏事,而是让我们有机会证明我们产品的价值。产品可以利用常见的认知偏见与用户建立信任,提高转化率和用户参与度,从而提高用户保留率。 -Because at the end of the day, it’s all in the packaging, and being 100% accurate and concise is simply not enough to persuade the users to give the product a chance or to try out its’ new features. +归根结底,就算一切都包装上,一个产品百分百如实简明的特征不足以说服用户去尝试使用其新功能。 -Users need more than that, and that’s exactly where **cognitive biases** can (and should) be used. +用户需要的不止于此,这正是**认知偏见**可以(也应该)使用的地方。 -Now, I have to warn you: this list is freaking long; it includes over 80 different biases - from biases we need to **avoid**, to biases we can **leverage** to improve our product's onboarding process, increase conversion rates, improve retention and generate more revenue. +现在,我必须提醒你:这个清单太长了。它包括超过 80 种不同的偏见 —— 从我们需要**避免**的偏见到我们可以**利用**的偏见,通过这些偏见来优化产品引流过程,提高转化率和保留率,并创造更多的收益。 -Luckily for you, I created **over 40 visual UI/UX examples** that will make it easier for you to skim through the list. Oh no, don't thank me! I'm just doing my job... +幸运的是,我创建了 40 多个可视化 UI/UX 示例来方便你的阅读。可别谢我!这是我该做的…… -### Who should read this list?  +### 谁需要阅读这个清单? -I believe **product managers**, **marketing managers**, and **entrepreneurs** will find this list extremely useful and relevant, but in fact, anyone who deals with **software development** might find something to relate to in this list. It helps everyone involved, understand the psychology behind users behavior and how to use it to build better products. +我相信**产品经理**、**市场经理**和**企业家**会发现这个清单与自己的工作息息相关且非常有用。但事实上,任何从事**软件开发**的人都可能会在这个清单中发现一些相关的内容。它可以帮助所有相关人员了解用户行为背后的用户心理,以及如何利用用户心理来构建更好的产品。 -Oh, and just to be clear, I did not invent these biases, I just collected them whenever I needed them in my work, and if you happen to disagree with some of them or think they don’t work, that’s probably because you are unique (or you may suffer from the Dunning-Kruger Effect as described below 😉). +哦,先说清楚,我没有发明这些偏见,我只是在工作需要时收集它们,如果你恰好不同意其中的一些观点,或者认为它们不起作用,那可能因为你与众不同(或者你可能会受到下文描述的达克效应的影响 😉)。 -OK, that turned out to be a pretty long opening. Let's begin: +好吧,这是一个相当长的开场白。我们开始吧: -## Superficial, are we? +## 我们肤浅吗? -The way the information is presented has a big impact on the way we think and make decisions. +信息的呈现方式非常影响我们的思考和决策。 -Sure, we say we are not superficial, but when it comes to products, the packaging plays a significant role. +当然,我们说我们并不肤浅,但一聊起产品,我们依旧认为包装非常重要。 -### 1. Streetlight Effect +### 1. 路灯效应 -We tend to search for things where it's easiest to look. +我们倾向于寻找最容易看到的东西。 -As the joke says: A policeman sees a drunk man searching for his wallet under a streetlight and asks: "_is this where you lost it?_", and the drunk replies, "_no, I lost it in the park, but this is where the light is_". +如同一则笑话里所说:一个警察看到一个醉汉在路灯下寻找他的钱包,问道:“这就是你丢钱包的地方吗?”,醉汉回答:“不,我把它丢在公园里了,但这里有灯光。” -**Product tip**: Whatever answers you are looking for: product, marketing, user satisfaction or anything else - dig deeper. Many answers are not where "the light" is, and analyzing data is often much harder than just collecting it. +**产品技巧**:无论你在寻找什么答案:产品、营销、用户满意度或其他任何东西,都要深入挖掘。许多答案并不在“光”所在之处,分析数据往往比收集数据要困难得多。 -### 2. Perceived Value Bias +### 2. 感知价值偏见 -We perceive the value of a product or service based on how it looks or how it’s served. +我们根据产品、服务的外观或服务方式来感知其价值。 -As they say: it’s all in the packaging! +正如他们所说:一切都在包装中! -**Product tip**: Design is more important for the success of your product than you think. That extra space, wrong border color, and misaligned text - they all impact your conversion rates. +**产品技巧**:设计对于产品成功的重要性超乎你的想象。多余的空白、不合适的边框颜色和错位的文本 —— 这些都会影响你的转化率。 -Make your UI design a priority. +优先考虑你的 UI 设计。 -![Perceived Value Bias - we perceive the value of a product or service based on how they look](https://alexdenk.eu/blogtouch?id=1pmZD59AgSE4oMG0lffF4T2X_zvYhwB54 "Perceived Value Bias - we perceive the value of a product or service based on how they look") +![感知价值偏见 —— 我们根据产品或服务的外观来感知其价值](https://alexdenk.eu/blogtouch?id=1pmZD59AgSE4oMG0lffF4T2X_zvYhwB54 "感知价值偏见 —— 我们根据产品或服务的外观来感知其价值") -Small UI corrections make a big impact +UI 的细微改动会产生巨大的影响。 -### 3. Picture Superiority Effect +### 3. 图优效应 -Pictures and images are more likely to be remembered than (a thousand) words. +图片和形象比长篇大论更容易被记住。 -**UI tip**: Always include images in your content. If you sell products or services - great visuals will improve your conversion rates. +**UI 技巧**:始终在内容中包含图像。如果你销售产品或服务 —— 出色的视觉效果将提高你的转化率。 -### 4. Von Restorff Effect (Isolation Effect) +### 4. 雷斯多夫效应(隔离效应) -When multiple homogeneous objects are presented together, the object that differs from the rest is more likely to be remembered. +当多个同类物品一起呈现时,与众不同的那个物品更容易被记住。 -**Design tip**: Make sure the CTA button stands out using a different style, size, color, and position. +**设计技巧**:确保行为召唤按钮使用不同的样式、大小、颜色和位置。 -![Von Restorff Effect - Make sure your CTA button stands out](https://alexdenk.eu/blogtouch?id=1EgHJ7W7O6bSIQihEHweO8Mp5IjI7a1gZ "Von Restorff Effect - Make sure your CTA button stands out") +![雷斯多夫效应 —— 确保你的行为召唤按钮突出显示](https://alexdenk.eu/blogtouch?id=1EgHJ7W7O6bSIQihEHweO8Mp5IjI7a1gZ "雷斯多夫效应 —— 确保你的行为召唤按钮突出显示") -Make sure your CTA buttons stand out +确保你的行为召唤按钮突出显示 -## More conservative than we think +## 我们比想象中更保守 -We say we are innovative, we like to try new technologies, but when it comes to core instincts and quick decision making - we aim to minimize risks and stick with the things we're already familiar with. +我们说自己是创新的,我们喜欢尝试新的技术,但当涉及到本能和快速决策时 —— 我们会倾向于选择低风险、已熟知的方式。 -### 5. Status Quo Bias +### 5. 现状偏差 -We tend to prefer a status quo over a change. +比起改变,我们倾向于保持现状。 -The current baseline is taken as a reference point, and any change from that baseline is perceived as a loss. +将当前的基准线作为参考点,与该基准线相比的任何变化都被视为一种损失。 -### 6. Endowment Effect +### 6. 禀赋效应 -Once we own something, we value it higher than we did before we owned it. +一旦我们拥有了某样东西,我们就会比拥有它之前更加珍惜它。 -As a result, we are more likely to retain an object we own than to acquire that same object if we don’t own it. +因此,面对同一个物品,我们倾向于继续保留已有的,而非再获取一个不属于我们的。 -Product tip: Free trials are the most common use of the endowment effect. +产品技巧:免费试用是禀赋效应最常见的用途。 -Once users commit to a certain product and invest time in it (i.e. build their profile etc.) it’s harder for them to let go and not upgrade their plan when the trial ends. +一旦用户专注于某一产品并投入时间(例如建立他们的个人资料等),他们就很难在试用期结束后收手。 -**Onboarding tip**: Find ways for people to play with your product before signing up. +**拉新技巧**:在用户注册前,让用户找到使用产品的方法。 -**Retention tip**: When a customer leaves you, provide examples of all the good things they’ll lose. +**留存技巧**:当用户离开时,展示他们将失去的所有好处。 -### 7. IKEA Effect +### 7. 宜家效应 -We place a disproportionately high value on products we created (or worked hard for). +我们对我们创造(或付出努力)的产品给予了过高的价值。 -**Product tip**: let your users do something as part of the onboarding process (not too hard, but rewarding) so they can connect with your product. +**产品技巧**:让你的用户在引导流程中做点什么(不要太难,而且要给予相应的奖励),这样他们就可以和你的产品建立起联系。 -### 8. Mere-Exposure Effect (Familiarity Principle) +### 8. 多看效应 (熟悉定律) -We tend to develop a preference for things we are familiar with. +我们倾向于偏爱我们熟悉的事物。 -**UI tip**: Stick with familiar UI concepts, behavior, terms, signs and icons. +**UI 技巧**:坚持使用用户熟悉的 UI 概念、行为、术语、符号和图标。 -Be consistent across your marketing materials, website and product to optimize the funnel. +在营销材料、网站和产品上保持一致来优化漏斗。 -**UX Writing tip**: Align with the industry jargon. Make sure your users feel comfortable. +**用户体验设计技巧**:用词保持和行业术语一致。确保你的用户感到舒适。 -![Mere-Exposure Effect - Stick with the UI standards](https://alexdenk.eu/blogtouch?id=1EPMg0K3076dsm2utSSKlAa4wMT-ig6H- "Mere-Exposure Effect - Stick with the UI standards") +![多看效应 —— 坚持 UI 标准](https://alexdenk.eu/blogtouch?id=1EPMg0K3076dsm2utSSKlAa4wMT-ig6H- "多看效应 —— 坚持 UI 标准") -Stick with the standards  +坚持标准 -### 9. Functional Fixedness +### 9. 功能定势 -We tend to use objects in the traditional way they were used. +我们倾向于用传统的方式使用事物。 -**Usability tip**: When your product challenges an existing usage tradition - it may cause some usability challenges. Keep them in mind and try to solve them in advance. +**可用性技巧**:当你的产品尝试打破现有的使用方式时 —— 它的可用性会面临一些挑战。将它们牢记在心,并尝试提前解决。 -### 10. Law of Instrument (Maslow's Hammer) +### 10. 工具规律(马斯洛的锤子) -We tend to over-rely on tools we are familiar with, even in the presence of much better options. +我们倾向于过度依赖我们熟悉的工具,即使我们有更好的选择。 -As they say: “_To a man with a hammer, everything looks like a nail_” +俗话说得好:“对于一个拿着锤子的人来说,一切都像是钉子。” -![To a man with a hammer - everything looks like a nail](https://alexdenk.eu/blogtouch?id=1NY8VZMcyZ9YBywLokF0wGnudItR-ZF_a "To a man with a hammer - everything looks like a nail") +![对于一个拿着锤子的人来说 —— 一切都像是钉子](https://alexdenk.eu/blogtouch?id=1NY8VZMcyZ9YBywLokF0wGnudItR-ZF_a "对于一个拿着锤子的人来说 —— 一切都像是钉子") -## Don’t call me a loser +## 别叫我失败者 -We hate losing more than we like winning. The right message (at the right timing) might turn on this aversion and push us to make a biased decision. +与其说我们喜欢成功,不如说我们讨厌失败。恰当的提示(出现在合适的时间)可能会激起这种反感的情绪,促使我们做出有偏见的决定。 -### 11. Loss Aversion +### 11. 损失规避 -We prefer not to lose $100 than to win $100 because the value of losing something is higher than the value of getting it (which is pretty consistent with the Endowment effect described above). +比起赢得 100 美元,我们更不愿意失去 100 美元。因为所失去东西的价值高于得到它的价值(这与上述的禀赋效应非常一致)。 -**UX Writing tip**: Use negative terms to express the potential loss: “stop wasting money”. +**用户体验设计小窍门**:使用负面词语来表达潜在的损失:“不要浪费钱”。 -**Product tip**: Limit your special deals (inside and outside your product) to create a sense of urgency: “this exclusive deal ends in x hours”. +**产品小窍门**:给你的特殊优惠加上限制 (在产品内外) 来制造紧迫感:“这个独家优惠将在 x 小时后结束”。 -![Loss Aversion - help your users avoid losses](https://alexdenk.eu/blogtouch?id=1-mEYTiDKektNhEGo5lOvz1w9oKrXYyoD "Loss Aversion - help your users avoid losses") +![损失规避 —— 帮助你的用户规避损失](https://alexdenk.eu/blogtouch?id=1-mEYTiDKektNhEGo5lOvz1w9oKrXYyoD "损失规避 —— 帮助你的用户规避损失") -Help your users avoid losses +帮助你的用户规避损失 -### 12. Zero-Risk Bias: +### 12. 零风险偏误 -We love certainty even if it's counterproductive. +哪怕事与愿违,我们也还是青睐确定的事。 -**Product tip**: offer money-back guarantee and risk-free trials to reduce the level of risks and make your customers feel secure. +**产品技巧**:提供退款保证和无风险试用来降低风险,让你的用户感到安全。 -![Zero-Risk Bias - Make sure all possible concerns are well addressed](https://alexdenk.eu/blogtouch?id=1pCFDq4W6nO7hR7HuCx9mIvqPiqu3y3Hm "Zero-Risk Bias - Make sure all possible concerns are well addressed") +![零风险偏误 —— 确保所有可能的问题都能得到妥善解决](https://alexdenk.eu/blogtouch?id=1pCFDq4W6nO7hR7HuCx9mIvqPiqu3y3Hm "零风险偏误 —— 确保所有可能的问题都能得到妥善解决") -Make sure all possible concerns are addressed +确保所有可能的问题都能得到妥善解决 -### 13. Neglect of Probability +### 13. 忽略可能性 -When we are under pressure, we fail to think of the probability of risks to happen. +当我们面临压力时,我们无法考虑风险发生的可能性。 -As a result, minor risks might get overrated or neglected. +因此,小风险可能会被高估或忽视。 -**Product rule**: during a conversion funnel - smallest uncertainties might cause the user to mistrust your product and stop. Make sure all details are clear and presented upfront. +**产品规则**:在转化漏斗中 —— 哪怕是一丁点的不确定性都可能导致用户不信任你的产品,从而停止使用。确保所有细节都清晰可见。 -Especially the ones that involve money such as the total cost, discounts (if exists), additional costs. +尤其是那些涉及金钱的信息细节,例如总成本、折扣(如果存在)、附加成本。 -![Neglect of Probability - Make sure to remove uncertainties in your product](https://alexdenk.eu/blogtouch?id=1tN2psH2vXjV6zgWBEjMouOFSi_A-ikxX "Neglect of Probability - Make sure to remove uncertainties in your product") +![忽略可能性 —— 确保从你的产品中移除不确定性](https://alexdenk.eu/blogtouch?id=1tN2psH2vXjV6zgWBEjMouOFSi_A-ikxX "忽略可能性 —— 确保从你的产品中移除不确定性") -Clear uncertainties proactively +清除不确定性 -Read: [7 must-have ingredients for creating a true mobile experience](https://www.mobilespoon.net/2019/03/7-unique-ingredients-mobile-app.html) +阅读:[创造真正移动体验的 7 个要素](https://www.mobilespoon.net/2019/03/7-unique-ingredients-mobile-app.html) -### 14. Scarcity Effect +### 14. 稀缺效应 -We place a higher value on an object that is scarce and a lower value on an object that is highly available. +物以少者为贵,多者为贱。 -The fear of missing out (FOMO) makes us more vulnerable to temptation and impulse and pushes us to make rush decisions. +错失恐惧症(FOMO)会让我们更容易受到诱惑和冲动的影响,迫使我们匆忙做出决定。 -**Product tip**: decorate your products and services with “limited time offers”, “limited quantity”, etc +**产品技巧**:用“限时优惠”、“数量有限”来装饰你的产品和服务。 -Create the impression that many others are watching this item “right now!” and about to grab the last remaining items any minute. +给人留下这样的印象:很多人都“正在!“盯着这个东西,并且随时都会买走最后剩下的物品。 -![Creating scarcity effect in UI](https://alexdenk.eu/blogtouch?id=15rOsh1pZOJQE_4Wb4LKFVxARJdwUpO7x "Creating scarcity effect in UI") +![在 UI 上制造稀缺效应](https://alexdenk.eu/blogtouch?id=15rOsh1pZOJQE_4Wb4LKFVxARJdwUpO7x "在 UI 上制造稀缺效应") -Or, as Booking.com would probably use it: +或者,正如 Booking.com 可能会使用的那样: -![Scarcity Effect - the drastic approach (as can be seen in well-known hotel booking apps](https://alexdenk.eu/blogtouch?id=1UmHrLdiyYGZhzvKGIw_etlOB-qAzveol "Scarcity Effect - the drastic approach (as can be seen in well-known hotel booking apps") +![稀缺效应 —— 激进的方法(可以在著名酒店的预订应用程序中看到)](https://alexdenk.eu/blogtouch?id=1UmHrLdiyYGZhzvKGIw_etlOB-qAzveol "稀缺效应 —— 激进的方法(可以在著名酒店的预订应用程序中看到)") -### 15. The Simulation Heuristic +### 15. 反事实思维 -We determine the likelihood of an event to happen based on how easy it is to picture the event mentally. As a result, "near misses" are more disappointing than other failures. +我们根据从心理上想象事件的容易程度来确定事件发生的可能性。因此,“未遂事件”比其他失败更令人失望。 -**Product tip**: Send a “You’re almost there!” email to users that almost completed an important action in your product, but eventually didn’t. Let them know they are very close, and that the opportunity is still waiting for them. +**产品技巧**:当用户在你的产品中几乎要完成一个重要的操作、但最终没有完成时,向用户发送“你即将达成!”的邮件。让用户知道他们已经非常接近了,机会仍然在等待着他们。 -Read: [Is 'the fold' still a thing in today’s scrolling and skimming culture?](https://www.mobilespoon.net/2019/05/fold-still-thing-in-todays-scrolling.html) +阅读:[当今滚动阅读的文化下还需要“首屏”吗?](https://www.mobilespoon.net/2019/05/fold-still-thing-in-todays-scrolling.html) -## Disproportionately emotional +## 比例过大的情绪化 -We try to make rational decisions, but sometimes our emotions are just stronger than we think. +我们试图做出理性的决定,但有时我们的情绪比我们想象的更加强烈。 -### 16. Negativity Bias +### 16. 负面偏差 -We give more weight to bad experiences than to good ones. +比起好的经历,我们更重视糟糕的经历。 -1 Negative Emotion = 3 x Positive Emotion +1 负面情绪 = 3 x 积极情绪 -![Negativity Bias: 1 negative emotion equals 3 positive ones](https://alexdenk.eu/blogtouch?id=1I8QoTHbfkB8tgLa0WMI1NRZpYM5hcZaF "Negativity Bias: 1 negative emotion equals 3 positive ones") +![负面偏差: 1 个负面情绪等于 3 个积极情绪](https://alexdenk.eu/blogtouch?id=1I8QoTHbfkB8tgLa0WMI1NRZpYM5hcZaF "负面偏差: 1 个负面情绪等于 3 个积极情绪") -**Product/marketing tip**: Illustrate your product value by the negative experience it resolves. +**产品/营销技巧**:通过解决负面情绪来体现产品的价值。 -If you hope your story to make an impact or even go viral - try emotionally negative content. +如果希望你的故事能产生影响,甚至像病毒一样传播开来 —— 尝试写一些负面情绪的内容。 -![Negativity Bias - Illustrate your product value by the negative experience it resolves.](https://alexdenk.eu/blogtouch?id=18_tQrUfk6v_Q6Y3IY4QrIfIvraHYRrXP "Negativity Bias - Illustrate your product value by the negative experience it resolves.") +![负面偏差 —— 通过强调其解决的负面体验来说明产品的价值。](https://alexdenk.eu/blogtouch?id=18_tQrUfk6v_Q6Y3IY4QrIfIvraHYRrXP "负面偏差 —— 通过强调其解决的负面体验来说明产品的价值。") -Illustrate the value of your product by emphasizing the negative experience it resolves  +通过强调其解决的负面体验来说明产品的价值 -### 17. Base Rate Fallacy (Base Rate Neglect) +### 17. 基本比率谬误(基本比率忽视) -We tend to ignore general information and focus on specific cases. +我们倾向于忽略一般信息并专注于具体案例。 -How to use: don’t just share cold information about your product. Instead, show testimonials, use cases, from people or companies others can relate with. +使用方法:不要只分享那些冷冰冰的产品信息。相反地,展示其他相关用户或公司的评价、用例。 -**Work-related tip**: If you want to be more convincing - blend your quantified data with some individual stories. Use quantified data to support your opinion rationally. The specific examples will convey your message emotionally. +**与工作相关的技巧**:如果你想更具有说服力 —— 把你的量化数据和一些个人故事相结合。用量化数据合理地支持你的观点。具体的例子会在情感上传递你想表达的信息。 -> If you want to be more convincing - blend your quantified data with some individual stories. Use quantified data to support your opinion rationally. The specific examples will convey your message emotionally. +> 如果你想更具有说服力 —— 把你的量化数据和一些个人故事相结合。用量化数据合理地支持你的观点。具体的例子会在情感上传递你想表达的信息。 -### 18. Identifiable Victim Effect +### 18. 可辨识受害者效应 -We tend to empathize more with a specific individual than with a large anonymous group. +我们更倾向于同情一个特定的人,而不是一个庞大的匿名群体。 -**How to use it**: When you are telling the story of your product, use personal stories of individuals instead of generic statements. +**如何使用它**:当你讲述产品故事时,使用个人的故事而不要用官方的陈述。 -![Identifiable Victim Effect - Use personal stories of individuals instead of generic statements. ](https://alexdenk.eu/blogtouch?id=1NZQMHBv_55MqCmi9ggDqCaLkDd31umgW "Identifiable Victim Effect - Use personal stories of individuals instead of generic statements. ") +![可辨识受害者效应 —— 使用个人的故事而不要用官方的陈述。](https://alexdenk.eu/blogtouch?id=1NZQMHBv_55MqCmi9ggDqCaLkDd31umgW "可辨识受害者效应 —— 使用个人的故事而不要用官方的陈述。") -Use personal stories instead of generic statements +使用个人的故事而不要用官方的陈述 -### 19. Likability Effect +### 19. 亲和力效应 -We like people who like the same things we do. +我们喜欢那些和我们拥有相同喜好的人。 -As an example, if you were to tell me you’re a Transformers fan - I would immediately count you as a friend. +举个例子,如果你告诉我你是变形金刚迷 —— 我会立刻把你当作朋友。 -**Marketing tip**: Show the strengths of your product by using testimonials of customers who faced similar issues as other potential customers. +**营销技巧**:通过使用与其他潜在客户面临类似问题的客户推荐来展现产品的优势。 -Make sure to use the authentic language customers are using - say it in their own words. +确保使用客户的真实语言 —— 用他们自己的话来表达。 -### 20. The Focusing Effect +### 20. 聚焦效应 -We attribute too much weight to events of the past and translate them into future expectations. +我们把过去的事情看得太重要,并将它们转变为对未来的期望。 -### 21. Impact Bias +### 21. 影响力偏差 -We tend to overestimate the length or the intensity of future emotional states. +我们倾向于高估未来情绪状态的持续时间或强度。 -It’s the “I’ll never get over her!” bias. +这是“我永远无法忘了她!”的偏见。 -**Possible usage**: Paint a picture of the users without your product or service, and later, introduce your product with how it will solve those pains. +**可能的用法**:描绘在没有你的产品或服务下用户会遭遇的问题,然后,介绍你的产品会如何解决这些问题。 -Read: [40 rules for designing and writing text in mobile apps](https://www.mobilespoon.net/2018/11/ux-writing-comprehensive-guide-for.html) +阅读:[在移动应用程序中设计和编写文本的 40 条规则](https://www.mobilespoon.net/2018/11/ux-writing-comprehensive-guide-for.html) --- -> * **[84 cognitive biases you should exploit to design better products - Part 1](https://github.com/xitu/gold-miner/blob/master/TODO1/collection-cognitive-biases-how-to-use-1.md)** -> * [84 cognitive biases you should exploit to design better products - Part 2](https://github.com/xitu/gold-miner/blob/master/TODO1/collection-cognitive-biases-how-to-use-2.md) -> * [84 cognitive biases you should exploit to design better products - Part 3](https://github.com/xitu/gold-miner/blob/master/TODO1/collection-cognitive-biases-how-to-use-3.md) +> * **[利用 84 种认知偏见设计更好的产品 —— 第一部分](https://github.com/xitu/gold-miner/blob/master/TODO1/collection-cognitive-biases-how-to-use-1.md)** +> * [利用 84 种认知偏见设计更好的产品 —— 第二部分](https://github.com/xitu/gold-miner/blob/master/TODO1/collection-cognitive-biases-how-to-use-2.md) +> * [利用 84 种认知偏见设计更好的产品 —— 第三部分](https://github.com/xitu/gold-miner/blob/master/TODO1/collection-cognitive-biases-how-to-use-3.md) --- -Follow me on twitter [@gilbouhnick](https://twitter.com/GilBouhnick), or [subscribe to my newsletter](https://mailchi.mp/b9c664dfafa3/mobilespoon) to get some occasional posts directly to your inbox. +在 Twitter 上关注我 [@gilbouhnick](https://twitter.com/GilBouhnick), 或 [订阅我的简报](https://mailchi.mp/b9c664dfafa3/mobilespoon),可以将帖子直接发送到你的收件箱。 > 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 From 3d8e2fb5f898f00f14ddd7a2b5ec4221a058059c Mon Sep 17 00:00:00 2001 From: sun <776766759@qq.com> Date: Mon, 15 Jul 2019 10:31:54 +0800 Subject: [PATCH 39/68] Create responsive-design-ground-rules.md (#6141) --- TODO1/responsive-design-ground-rules.md | 88 +++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 TODO1/responsive-design-ground-rules.md diff --git a/TODO1/responsive-design-ground-rules.md b/TODO1/responsive-design-ground-rules.md new file mode 100644 index 00000000000..832184af45d --- /dev/null +++ b/TODO1/responsive-design-ground-rules.md @@ -0,0 +1,88 @@ +> * 原文地址:[Responsive design ground rules](https://polypane.rocks/blog/responsive-design-ground-rules/) +> * 原文作者:[Polypane](https://polypane.rocks/blog/) +> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) +> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/responsive-design-ground-rules.md](https://github.com/xitu/gold-miner/blob/master/TODO1/responsive-design-ground-rules.md) +> * 译者: +> * 校对者: + +# Responsive design ground rules + +Creating a responsive design can be intimidating. There are many moving parts, things might lay out in ways you didn't expect and keeping all various viewports in mind when laying out a design can be daunting. With these ground rules, your responsive designs will be more robust and predictable. + +## Rule #1: Keep your viewport simple + +Back when the viewport meta tag was first introduced, common knowledge was you had to add in all sorts of values to prevent users from resizing and to have a minimum and maximum screen size. It turns out that doing that is actually hostile to your users. + +You really only want two things: set the width to the device your site is shown on, and make sure the initial scale is 1, which means that 1 pixel in your CSS equals one device-independent pixel, like this: + +``` + +``` + +## Rule #2: Mobile first + +You develop websites on a large laptop or desktop screen, and usually your client is most interested in the desktop design of a website, so it might feel natural to just start with the design for the desktop site and then work your way down. But starting mobile first is actually easier and will result in less code. + +If you build mobile first, you're building up your CSS in complexity. What I mean with that is that your mobile views are usually much simpler and thus require less CSS. They almost always have just a single column, and lack many of the additional flourishes you have space for on larger screens. If you build mobile-first, this means that, as you add styling for larger and larger media queries, you're **adding** to the design. + +If you start desktop first, you already have all this styling that you then need to write **more** CSS for just to undo your more advanced desktop styling. So you're writing more CSS and if you're not carefully undoing all CSS, you end up with things like horizontal overflowing or text not fitting. + +With mobile first, you save a large chunk of CSS you simply **don't have to write**, making it smaller and your website faster. + +## Rule #3: Design from content out + +To determine where your breakpoint will be, you can opt to use values like 320px, 375px, 768px and 1024px, which all map to various real device widths. Basically, design for specific devices. But when new devices become more popular **(#375IsTheNew320)** your design might not look so good on those devices. + +[Stephen Hay](http://the-haystack.com/), who wrote the book on [responsive design workflows](http://www.peachpit.com/store/responsive-design-workflow-9780321887863), advices you to start with your small screen, then "expand until it looks like shit. Time for a breakpoint!" + +This focus on the content will force you to think of websites as inherently fluid. You can't design only your pixel perfect widths, because **they don't exist**. + +Quick rule of thumb: you want your line lengths to be around 70 characters long. That translates (depending on the font!) to about 36 to 40 em. + +## Rule #4: Use ems in your media queries + +With specific device widths no longer mattering, you should also switch out those breakpoint widths in pixels, to **widths in ems**. Your media queries are based on the content so this will let your site look great even for people that have made their browsers base font-size larger or smaller or have zoomed in their browser. + +The rest of your design will properly adjust to this and make your site more robust. + +## Rule #5: Min-width or max-width, pick one + +Responsive design makes for an incredibly complex system. When your media queries use both min-width and max-width, or even combinations of them, you're massively increasing that complexity and reasoning about it becomes even harder. + +If all your media queries work "up" or "down", you always know where to look when your site doesn't look as you expect it to at a certain size. CSS in new media queries you write will then never influence your earlier sizes. Just find out from which media query down (or up) you need to update your CSS. + +## Rule #6: Avoid fixed dimensions + +It can be very tempting to use fixed dimensions for elements. After all, your favorite hand-off tool probably lets you copy them with ease. Elements with fixed widths (or margins) could easily break your layout if you're not careful. + +Try to style element sizes in relation to their surroundings. Use percentages or viewport units. Prevent setting `width` and `height` and try to use their `min-` and `max-` counterparts. And if you do end up with a `width` breaking something somewhere, a `max-width:100%`can work wonders. + +## Rule #7: Use modern layout techniques + +To expand on the previous rule, modern layout methods like Flexbox and CSS Grid are built to be inherently flexible and size according to their surroundings. If you make use of these layout methods, you'll end up needing less media queries to achieve the same design. Less media queries means less to reason about, and your code's shorter to boot. + +A great way to (re-)learn how to build common layouts with Flexbox and CSS Grid is [Every-layout.dev](https://every-layout.dev/). It lists comon layouts and explains how to build them using modern techniques. + +## Rule #8: Leave room for text rendering differences + +It's tempting to create breakpoints right at the place where an unfavorable line-break occurs. To get that "pixel perfect". Of course we know the web isn't pixel perfect, and it never was. + +If your breakpoints are too close to readable line breaks, then it might work in **your** browser, but different browsers and different operating systems have different ways of rendering text, which means that the line of text might be a couple of pixel wider or smaller,, and your design could break. + +Instead, try to be a little bit loose with your media queries, leave a little space for things to be off by a few pixels before big changes in your design. + +## Rule #9: Decide in the browser + +To follow these rules, it doesn't make sense to create all your breakpoints in a design tool. On the other hand, designing the entire site in a browser is difficult too. So what's the happy medium? + +Create your designs in a design tool, with some rough responsive variants, but keep the choice of **when** to switch over to another design for when you're actually working in the browser. The Sketch artboard might be 750px wide, but if you're in the browser and the layout already makes more sense at 44em (that's 704 pixels), then use `44em` in your css. + +## Rule #10: Give Polypane a try + +With Polypane, creating sites and apps in a mobile-first, content-out way comes naturally. Start with a single pane and design your smallest screen. Then add a new pane, and widen it until, to quote Stephen, it "looks like shit". Then check the width of the pane and use that `em` value as your new breakpoint. Style it and repeat. + +> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 + +--- + +> [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[区块链](https://github.com/xitu/gold-miner#区块链)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计)、[人工智能](https://github.com/xitu/gold-miner#人工智能)等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。 From 3273e43e3f22f8c007dacec5bae1e4d3dad3ffbb Mon Sep 17 00:00:00 2001 From: lsvih Date: Mon, 15 Jul 2019 16:01:54 +0800 Subject: [PATCH 40/68] Create 16-devtools-tips-and-tricks-every-css-developer-need-to-know.md (#6143) * Create 16-devtools-tips-and-tricks-every-css-developer-need-to-know.md * Update 16-devtools-tips-and-tricks-every-css-developer-need-to-know.md * Update 16-devtools-tips-and-tricks-every-css-developer-need-to-know.md --- ...tricks-every-css-developer-need-to-know.md | 212 ++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 TODO1/16-devtools-tips-and-tricks-every-css-developer-need-to-know.md diff --git a/TODO1/16-devtools-tips-and-tricks-every-css-developer-need-to-know.md b/TODO1/16-devtools-tips-and-tricks-every-css-developer-need-to-know.md new file mode 100644 index 00000000000..09187dde69b --- /dev/null +++ b/TODO1/16-devtools-tips-and-tricks-every-css-developer-need-to-know.md @@ -0,0 +1,212 @@ +> * 原文地址:[16 DevTools tips and tricks every CSS developer needs to know](https://www.heartinternet.uk/blog/16-devtools-tips-and-tricks-every-css-developer-need-to-know/) +> * 原文作者:[Louis Lazaris](https://www.heartinternet.uk/blog/author/louis-lazaris/) +> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) +> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/16-devtools-tips-and-tricks-every-css-developer-need-to-know.md](https://github.com/xitu/gold-miner/blob/master/TODO1/16-devtools-tips-and-tricks-every-css-developer-need-to-know.md) +> * 译者: +> * 校对者 + +# 16 DevTools tips and tricks every CSS developer needs to know + +When it comes to debugging the front-end, if you're like many developers, you basically live in your browser's developer tools. But even after having worked in Chrome's developer tools for quite a few years, I still come across tips, tricks, and features that I haven't seen before. + +In this article, I've compiled a number of CSS-related features and tricks available via developer tools that I think will take your CSS development to a new level. Some of these tips aren't specifically only for CSS, but I'll present them in a CSS context. + +Some are simple tips for workflow and debugging, while others are new features that have rolled out in recent years. Most are based on Chrome's DevTools, but I've also included a few Firefox tips. + +## Examine CSS for an element that appears via JavaScript + +Finding the CSS for most elements in the DevTools Elements panel isn't difficult. In most cases you can just right-click the element, inspect it, then (if necessary) drill down to find it in the Elements panel. Once it's selected, the CSS will appear in the Styles panel, ready to edit. + +Sometimes an element appears dynamically as a result of some JavaScript-based user action like click or mouseover. The most obvious way to make the element appear is to temporarily alter your JavaScript and/or CSS to make the element visible by default, so you can deal with it without needing to mimic the user action. + +But if you're looking for a quick way to make the element visible using just your DevTools, here are the steps to do this: + +1. Open DevTools +2. Open the Sources panel +3. Carry out the user action to make the element visible (e.g. mouseover) +4. Hit F8 (same as "Pause script execution" button) while the element is visible +5. Click the "Select an element..." button in DevTools +6. Click the element on the page + +We can test this using [Bootstrap's tooltips](https://getbootstrap.com/docs/3.3/javascript/#tooltips), which only appear when hovering over a link, triggered via JavaScript. Here's a demonstration: + +![Animated GIF showing how to select an element using Bootstrap's tooltips](https://www.heartinternet.uk/blog/wp-content/uploads/bootstrap-tool-tips-example.gif) + +As you can see at the start of the video, I can't initially reach the element to inspect it, because it disappears on mouseout. But if I stop script execution while it's visible, it will stay visible so I can inspect it properly. Of course, if it was simply a CSS `:hover` effect, then I could make it appear using the "Toggle element state" section of the Styles panel. But in this case, this is probably the best way to grab the styles of an element whose visibility is triggered via JavaScript. + +## Search for an element using CSS selectors + +You might know that if you want to search for an element in the Elements panel, you can do this using a find feature (CTRL-F/CMD-F) that's built in. But notice when you view the "find" field, it gives you the following instructions: + +![Image showing where you can search for an element using CSS selectors](https://www.heartinternet.uk/blog/wp-content/uploads/search-for-a-css-element.png) + +As I've indicated in the screenshot, you can find an element "by string, selector, or XPath". I've used "string" many times before but only recently realised I can use a selector for this. + +You don't have to use a selector that's in use in the CSS, it could be any valid CSS selector. The find feature will tell you if your selector matches any elements. This could be useful for finding elements but might also help for testing selectors to see what works. + +Below is a demo that uses the selector `body>div` to search and cycle through all the `div` elements on the page that are direct children of the `body` element: + +![Animated GIF showing how to search through specific selectors in your CSS](https://www.heartinternet.uk/blog/wp-content/uploads/body-div-seach-example.gif) + +As mentioned, this search can be done with any valid selector, which would be similar to using JavaScript's `querySelector()` or `querySelectorAll()` methods. + +## Edit the box model directly + +The box model is one of the first things you learn when you start out with CSS. As this is an important aspect of CSS layouts, the DevTools allow you to edit the box model directly. + +If you inspect an element on the page, in the right panel click the "Computed" panel next to the "Styles" panel. This shows you a visual interpretation of the box model for that element, with the values as part of the graphic: + +![An image showing the visual representation of the box model for that particular element](https://www.heartinternet.uk/blog/wp-content/uploads/model-box-example.png) + +But maybe you didn't know that you can edit any of those values in place by double clicking them: + +![Animated GIF showing how you can edit the values of the box model within the representation](https://www.heartinternet.uk/blog/wp-content/uploads/model-box-editing-example.gif) + +Any changes made are reflected on the page in the same way as when you edit the CSS in the Styles panel. + +## Increment/decrement values in the Styles panel + +You are probably already aware that you can edit your CSS in the Styles panel. Just click on a property or value and type in your changes. + +But maybe you didn't realise that numerical values can be incremented or decremented in different ways. + +- Up/Down arrow keys increment/decrement by 1 +- ALT+Up/Down arrow keys increment/decrement by 0.1 +- SHIFT+Up/Down arrow keys increment/decrement by 10 +- CTRL+Up/Down arrow keys increment/decrement by 100 + +![Animated GIF showing how you can increase or decrease the values using the arrow keys](https://www.heartinternet.uk/blog/wp-content/uploads/incrementing-values-in-the-styles-panel-example.gif) + +You can also use the Page Up or Page Down buttons instead of the arrow keys. + +## Text editor-like features in Sources panel + +You're probably more familiar with making edits in the Styles panel than anywhere else. The Sources panel, however, is a highly underrated feature of the DevTools because of how closely it mimics working in a regular code editor or IDE. + +Here are some of the useful things you can do in the Source panel (which you can view by opening DevTools and clicking the "Sources" tab). + +### Make multiple selections with the CTRL key + +If you need to select multiple areas in a single file, you can do this by holding the CTRL key and selecting what you want, even if it's not contiguous text. + +![Animated GIF showing how multiple selections can be made by holding the CRTL key](https://www.heartinternet.uk/blog/wp-content/uploads/multiple-selections-with-ctrl-key.gif) + +In the above demo I'm selecting three arbitrary parts of the main.css file in the Sources panel and pasting them back into the document. In addition, you can also type in multiple spots at the same time with multiple carets. Again, use the CTRL key to click in multiple spots to make identical edits to all indicated locations. + +### Column selection with ALT key + +In some cases, you might want to select a column of text, which you can't normally do by default. Some text editors allow you to use the ALT key to accomplish this, and the same is true in the Sources panel. + +![Animated GIF showing how an entire column can be selected by using the ALT key](https://www.heartinternet.uk/blog/wp-content/uploads/column-selection-with-alt-key.gif) + +## Search by CSS selector with CTRL-SHIFT-O + +With a file open in the Sources panel, press CTRL-SHIFT-O on your keyboard to open up the "Goto Anything" box, which is a well-known feature in Sublime Text Editor. + +After hitting CTRL-SHIFT-O, you can type a CSS selector that you want to find in the file, and the DevTools will give you options to choose to jump to a specific part of the file. + +![Animated GIF showing how to find a specific CSS selector in the file](https://www.heartinternet.uk/blog/wp-content/uploads/search-with-css-selector-shortcut.gif) + +## Responsive design features in Chrome and Firefox + +You've probably seen one of those websites that lets you test the responsiveness of your layout right in the browser with a few clicks. Well, you can do the same thing with Chrome's Device Mode. + +Just open the DevTools and click the "Toggle device toolbar" button in the top-left area of the DevTools (CTRL-SHIFT-M will also do it): + +![Animated GIF showing how to test the responsiveness of a site in Chrome's Device Mode](https://www.heartinternet.uk/blog/wp-content/uploads/testing-responsive-design.gif) + +As you can see, the device toolbar has multiple options to change the view according to device size and device type, and you can even make those changes manually by adjusting the width and height numbers or by dragging the handles in the viewport area. + +Firefox has a similar feature with the added "@media rules" panel that allows you to click on a breakpoint from the site's stylesheet. You can see me using this on one of my websites in the demo below. + +![Animated GIF showing how to test the responsiveness of a site in Firefox](https://www.heartinternet.uk/blog/wp-content/uploads/firefox-responsive-design-test.gif) + +## Colour features in DevTools + +Dealing with colour values in CSS is a constant. DevTools makes it so much easier to edit, test, and otherwise fiddle with colour values. Here are some things you can do. + +### Contrast ratio + +First, there are the accessibility features. When you see a colour value in the Styles panel, you can click the swatch next to the colour value to open the colour picker. Inside the colour picker, you'll see a contrast ratio option that indicates whether your choice of text colour has an accessible contrast in relation to the background. + +![Animated GIF showing the accessible contrast for a particular colour](https://www.heartinternet.uk/blog/wp-content/uploads/css-contrast-ratio.gif) + +As you can see in the above demo, the colour picker shows a curved white line in the colour spectrum. This line indicates where the minimum acceptable contrast ratio begins and ends. When I move the colour value above the white line, the contrast ratio value loses its green checkmark, indicating poor contrast. + +### Colour palettes + +In addition to the accessibility features, you also have access to different colour palettes, including a Material Design palette and one associated with the currently viewed page. + +![Animated GIF showing a colour palette for a particular colour](https://www.heartinternet.uk/blog/wp-content/uploads/css-colour-palettes.gif) + +### Toggling colour value syntax + +Finally, one little-known tidbit on viewing colour values in DevTools is the ability to switch the syntax of a specific colour value. By default, the Styles panel will show the syntax for the colour as it was written in the CSS. But the DevTools let you toggle between hex, RGBA, an HSLA by  holding the shift key and clicking the swatch next to the colour value: + +![Animated GIF showing how you can toggle the syntax of a colour value](https://www.heartinternet.uk/blog/wp-content/uploads/toggling-colour-value-syntax.gif) + +## Editing CSS shadows + +Text shadows and box shadows can be tedious to try to write out in your CSS by hand. The syntax is easy to forget and the two kinds of shadows have slightly differing syntax. + +Conveniently, the Chrome DevTools allow you to add a text shadow or box shadow using a visual editor. + +![Animated GIF showing how to edit a shadow effect in Chrome DevTools](https://www.heartinternet.uk/blog/wp-content/uploads/editing-css-shadows.gif) + +As shown in the demo, you can add a box shadow or text shadow to any element using the option bar that appears at the bottom right corner of any style rule in the Styles panel. After the shadow is added, you can modify the shadow's various properties using a visual editor. This editor can then be brought up again for any existing shadow using the "Open shadow editor" option next to the property name. + +## Grid Layout Inspector in Firefox + +Now that Grid Layout is supported in the majority of in-use browsers, more and more developers are using it as their default layout method. Firefox's developer tools now feature a "Grid" section in the "Layout" tab of the developer tools. + +![Animated GIF showing how to use the Grid Layout Inspector in Firefox](https://www.heartinternet.uk/blog/wp-content/uploads/grid-layout-inspector-in-firefox.gif) + +This feature allows you to enable an overlay grid that helps to visualise the different parts of your grid layout. You can also display line numbers, area names, and you can choose to extend the grid lines infinitely if that helps you. In the example demo, I'm using this example site by Jen Simmons, which is responsive, so you can see the benefits of the overlay when the layout changes for different sized viewports. + +## CSS filters editor in Firefox + +Filters are another feature that now have near-universal support in both mobile and desktop browsers. Once again, Firefox offers a handy little tool to help you edit your filter values. + +Once you've got a filter in place in your code (tip: you can start with `filter: none` if you don't know the syntax for an actual filter from memory), you'll notice a gray and white swatch next to the filter's value. Click that swatch to open the filter editor. + +![Animaged GIF showing how to use the Firefox CSS filters editor](https://www.heartinternet.uk/blog/wp-content/uploads/css-filter-editor-in-firefox.gif) + +You can add multiple filters to a single value, delete individual filter values, and you can drag and drop individual filters to rearrange the order in which they're applied. + +![Aniamted GIF showing how to drag and drop individual filters on a single element](https://www.heartinternet.uk/blog/wp-content/uploads/css-multiple-filters-in-firefox.gif) + +## Edit CSS animations in Chrome's Styles panel + +Static elements are fairly straightforward to edit in the Styles panel in Chrome's DevTools. But what about animations created using the `animation` property and the `@keyframes` at-rule? + +DevTools has two ways you can edit animations. First, when you inspect an element or select it in the Elements panel, all the element's styles appear in the Styles panel -- including the defined `@keyframes`. In the following demo, I'm selecting an animated element, then adjusting and fiddling with some of the keyframe settings. + +![Animated GIF showing how to edit CSS animations in Chrome's Styles panel](https://www.heartinternet.uk/blog/wp-content/uploads/editing-animation-keyframe-settings-in-chrome.gif) + +But that's not all. Chrome's DevTools also features an Animation panel that allows you to edit an animation and its various parts using a visual timeline. You can access this feature by going into the "Customize and control DevTools" option (the three dots), choosing "More tools", and selecting "Animations". + +![Animated GIF showing the Animations panel in Chrome's DevTools](https://www.heartinternet.uk/blog/wp-content/uploads/editting-css-animations-in-chrome-style-panel.gif) + +As shown above, you can edit each individual animated element's timeline, then once your edits are complete, you can scrub through the animation to see the changes live on the page. This is a cool feature for designing and debugging complex CSS animations! + +## View unused CSS in DevTools + +Lately there's been an influx of tools that help you track down parts of your CSS not used on specific pages. This way you can choose to either remove them altogether or load them only when necessary. This will have clear performance advantages. + +Chrome allows you to view unused CSS right inside the DevTools by means of the "Coverage" panel. This panel can be opened by clicking the "Customize and control DevTools" (three dots) option, then "More tools", then "Coverage". + +![Animated GIF showing how to customise your menu on Chrome's DevTools](https://www.heartinternet.uk/blog/wp-content/uploads/view-unused-css-in-dev-tools.gif) + +As shown in the demo, once you're in the Coverage panel, you can select a source file to open it in the "Sources" panel. When the file opens in Sources, you'll notice green and red indicator lines next to each style rule in the CSS file. These indicate if that part of the CSS was used on the current page. + +## Conclusion + +Your browser's developer tools are a treasure trove of CSS editing and debugging features. And when you combine these suggestions with a feature in Chrome like Workspaces (which allows you to save changes made in DevTools to a local file), the process of debugging becomes even more complete. + +I hope these tips and suggestions will enhance your CSS editing and debugging abilities in your future projects. + +> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 + +--- + +> [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[区块链](https://github.com/xitu/gold-miner#区块链)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计)、[人工智能](https://github.com/xitu/gold-miner#人工智能)等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。 From 5144968d11c66a3c1587740bac754c363527fbe1 Mon Sep 17 00:00:00 2001 From: LeviDing Date: Mon, 15 Jul 2019 20:21:29 +0800 Subject: [PATCH 41/68] Create hermes.md (#6145) * Create hermes.md * Update hermes.md --- TODO1/hermes.md | 104 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 TODO1/hermes.md diff --git a/TODO1/hermes.md b/TODO1/hermes.md new file mode 100644 index 00000000000..d97e0d1bc80 --- /dev/null +++ b/TODO1/hermes.md @@ -0,0 +1,104 @@ +> * 原文地址:[Hermes: An open source JavaScript engine optimized for mobile apps, starting with React Native](https://code.fb.com/android/hermes/) +> * 原文作者:[Marc Horowitz](https://code.fb.com/android/hermes/) +> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) +> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/hermes.md](https://github.com/xitu/gold-miner/blob/master/TODO1/hermes.md) +> * 译者: +> * 校对者: + +# Hermes: An open source JavaScript engine optimized for mobile apps, starting with React Native + +![](https://code.fb.com/wp-content/uploads/2019/07/HermesOSSChainReact_blog_FIN_1-1.gif) + +Mobile applications are growing larger and more complex. Larger apps using JavaScript frameworks often experience performance issues as developers add features and complexity. These issues are generated from various spots, but the people using these apps expect them to run smoothly, regardless of the device they are on. + +To increase the performance of Facebook’s apps, we have teams that continuously improve our JavaScript code and platforms. As we analyzed performance data, we noticed that the JavaScript engine itself was a significant factor in startup performance and download size. With this data in hand, we knew we had to optimize JavaScript performance in the more constrained environments of a mobile phone compared with a desktop or laptop. After exploring other options, we built a new JavaScript engine we call Hermes. It is designed to improve app performance, focusing on our React Native apps, even on mass-market devices with limited memory, slow storage, and reduced computing power. + +At [Chain React 2019](https://infinite.red/ChainReactConf), we announced the Hermes JavaScript engine. We have [open-sourced the Hermes engine](https://github.com/facebook/hermes), as well as [integration with Hermes for React Native](https://facebook.github.io/react-native/docs/hermes/). We are excited to work with the open source community and have developers start using Hermes today. + +## How Hermes improves React Native performance + +For JavaScript-based mobile applications, user experience benefits from attention to a few primary metrics: + +* The time it takes for the app to become usable, called time to interact (TTI) +* The download size (on Android, APK size) +* Memory utilization + +![](https://code.fb.com/wp-content/uploads/2019/07/hermesstats-1.jpg) + +Metrics for MatterMost React Native app running on a Google Pixel, similar in performance to popular phones in markets like India. + +Notably, our primary metrics are relatively insensitive to the engine’s CPU usage when executing JavaScript code. Focusing on these metrics leads to strategies and trade-offs that differ from most existing JavaScript engines today. Consequently, our team designed and built Hermes from scratch. As a result of this focus, our implementation provides substantial improvement for React Native applications.  + +Because Hermes is optimized for mobile apps, we do not have plans to integrate it with any browsers or with server infrastructure such as Node.js. Existing JavaScript engines remain preferable in those environments. + +## Key Hermes architectural decisions + +Mobile device limitations, such as smaller amounts of RAM and slower flash, led us to make certain architectural decisions. To optimize for this environment, we implemented the following: + +### Bytecode precompilation + +Commonly, a JavaScript engine will parse the JavaScript source after it is loaded, generating bytecode. This step delays the start of JavaScript execution. To skip this step, Hermes uses an ahead-of-time compiler, which runs as part of the mobile application build process. As a result, more time can be spent optimizing the bytecode, so the bytecode is smaller and more efficient. Whole-program optimizations can be performed, such as function deduplication and string table packing. + +The bytecode is designed so that at runtime, it can be mapped into memory and interpreted without needing to eagerly read the entire file. Flash memory I/O adds significant latency on many medium and low-end mobile devices, so loading bytecode from flash only when needed and optimizing bytecode for size leads to significant TTI improvements. In addition, because the memory is mapped read-only and backed by a file, mobile operating systems that don’t swap, such as Android, can still evict these pages under memory pressure. This reduces out-of-memory process kills on memory constrained devices. + +![](https://code.fb.com/wp-content/uploads/2019/07/HermesOSSChainReact_blog_FIN_1-1.gif) + +Although compressed bytecode is a bit larger than compressed JavaScript source code, because Hermes’s native code size is smaller, Hermes decreases overall application size for Android React Native apps. + +### No JIT + +To speed execution, most widely used JavaScript engines can lazily compile frequently interpreted code to machine code. This work is performed by a just-in-time (JIT) compiler. + +Hermes today has no JIT compiler. This means that Hermes underperforms some benchmarks, especially those that depend on CPU performance. This was an intentional choice: These benchmarks are generally not representative of mobile application workloads. We have done some experimentation with JITs, but we believe that it would be quite challenging to achieve beneficial speed improvements without regressing our primary metrics. Because JITs must warm up when an application starts, they have trouble improving TTI and may even hurt TTI. Also, a JIT adds to native code size and memory consumption, which negatively affects our primary metrics. A JIT is likely to hurt the metrics we care about most, so we chose not to implement a JIT. Instead, we focused on interpreter performance as the right trade-off for Hermes. + +### Garbage collector strategy + +On mobile devices, efficient use of memory is especially important. Lower-end devices have limited memory, OS swapping does not generally exist, and operating systems aggressively kill applications that use too much memory. When apps are killed, slow restarts are required and background functionality suffers. In early testing, we learned that virtual address (VA) space, especially contiguous VA space, can be a limited resource in large applications on 32-bit devices even with lazy allocation of physical pages.  + +To minimize memory and VA space used by the engine, we have built a garbage collector with the following features: + +* On-demand allocation: Allocates VA space in chunks only as needed. +* Noncontiguous: VA space need not be in a single memory range, which avoids resource limits on 32-bit devices. +* Moving: Being able to move objects means memory can be defragmented and chunks that are no longer needed are returned to the operating system. +* Generational: Not scanning the entire JavaScript heap every GC reduces GC times. + +## Developer experience + +To start using Hermes, developers will need to make a few changes to their `build.gradle` files and recompile the app. See the [full instructions for the migration to use Hermes on React Native.](https://facebook.github.io/react-native/docs/hermes/) + +```javascript + project.ext.react = [ + entryFile: "index.js", + enableHermes: true +] +``` + +### Lazy compilation + +Iteration speed is one of the main benefits of a JavaScript-based platform, but compiling bytecode in advance would chip away at this advantage. To keep reloads fast, Hermes debug builds don’t use ahead-of-time compilation; instead, they generate bytecode lazily on device. This allows for rapid iteration using Metro or another source of plain JavaScript code to run. The trade-off is that lazy-compiled bytecode does not include all the optimizations of a production build. In practice, although we can measure the difference in performance, we have found this approach is sufficient to provide a good developer experience without affecting production metrics. + +### Standards-compliant + +Hermes currently targets the ES6 specification, and we intend to keep current with the JavaScript specification as it evolves. To keep the engine’s size small, we have chosen not to support a few language features that do not appear to be commonly used in React Native apps, such as proxies and local `eval()`. A [complete list can be found at our GitHub](https://github.com/facebook/hermes/blob/master/doc/Features.md#excluded-from-support). + +### Debugging + +To provide a great debugging experience, we implemented support for Chrome remote debugging via the DevTools protocol. Until today, React Native has supported debugging using only an in-app proxy to run the application JavaScript code in Chrome. This support made it possible to debug apps, but not synchronous native calls in the React Native bridge. Support for the remote debugging protocol allows developers to attach to the Hermes engine running on their device and debug their applications natively, using the same engine as in production. We are also looking into implementing additional support for the Chrome DevTools protocol besides debugging. + +![](https://code.fb.com/wp-content/uploads/2019/07/Hermes-screenshot.jpg) + +## Enabling improvements to React Native + +To ease migration efforts to Hermes and continue supporting JavaScriptCore on iOS, we built JSI, a lightweight API for embedding a JavaScript engine in a C++ application. This API has made it possible for React Native engineers to implement their own infrastructure improvements. JSI is used by Fabric, which allows for preemption of React Native rendering, and by TurboModules, which allow for lighter weight native modules that can be lazy loaded as needed by a React Native application. + +React Native was our initial use case and has informed much of our work to date, but we aren’t stopping there. We intend to build time and memory profiling tools to make it easier for developers to improve their applications. We would like to fully support the Visual Studio Code debugger protocol, including completion and other features not available today. We’d also like to see other mobile use cases.  + +No open source project can be successful without engagement from the community. We’d love for you to [try Hermes in your React Native apps](https://facebook.github.io/react-native/docs/hermes/), see how it works, and help us [make Hermes better for everyone](https://github.com/facebook/hermes/issues). We are especially interested in seeing which use cases the community finds useful, both inside and outside of React Native. + +We’d like to thank Tzvetan Mikov, Will Holen, and the rest of the Hermes team for their work to build and open-source Hermes. + +> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 + +--- + +> [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[区块链](https://github.com/xitu/gold-miner#区块链)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计)、[人工智能](https://github.com/xitu/gold-miner#人工智能)等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。 From 1ba28c1e567b323d9318edc4e3963b8499e952a0 Mon Sep 17 00:00:00 2001 From: Charlo <49369951+Charlo-O@users.noreply.github.com> Date: Tue, 16 Jul 2019 10:57:53 +0800 Subject: [PATCH 42/68] =?UTF-8?q?=E6=95=8F=E6=8D=B7=E4=B9=9F=E8=AE=B8?= =?UTF-8?q?=E6=98=AF=E4=B8=AA=E9=97=AE=E9=A2=98=20(#6105)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 敏捷也许是个问题 敏捷也许是个问题 * Update agile-agile-blah-blah.md * Update agile-agile-blah-blah.md * Update agile-agile-blah-blah.md --- TODO1/agile-agile-blah-blah.md | 110 ++++++++++++++++----------------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/TODO1/agile-agile-blah-blah.md b/TODO1/agile-agile-blah-blah.md index 9555543b9b0..8db9470b962 100644 --- a/TODO1/agile-agile-blah-blah.md +++ b/TODO1/agile-agile-blah-blah.md @@ -2,111 +2,111 @@ > * 原文作者:[Mo Hagar](https://www.infoq.com/profile/Mo-Hagar/) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/agile-agile-blah-blah.md](https://github.com/xitu/gold-miner/blob/master/TODO1/agile-agile-blah-blah.md) -> * 译者: -> * 校对者: +> * 译者:[Charlo](https://github.com/Charlo-O) +> * 校对者:[Gavin](https://github.com/redagavin) -# Maybe Agile Is the Problem +# 敏捷也许是个问题 -### Key Takeaways +### 关键点 -* Many organizations are Agile fatigued  -* The “Agile Industrial Complex” is part of the problem -* Agilists must go back to the basics and simplicity of the Manifesto and 12 Principles -* The Heart of Agile and Modern Agile are examples of basic, simple frameworks -* Agilists have much to learn from social sciences such as Positive Psychology, Appreciative Inquiry, and Solution Focus +* 许多组织都厌倦了敏捷  +* “敏捷工业综合体”是问题的一部分 +* 敏捷开发者必须回归到宣言和十二准则的简单基础上 +* [Heart of Agile](https://heartofagile.com/) 和 [Modern Agile](http://modernagile.org/) 是基本、简洁框架的典范 +* 敏捷者需要从社会科学中学习很多东西,比如[积极心理学](https://en.wikipedia.org/wiki/Positive_psychology)、[欣赏式探询](https://en.wikipedia.org/wiki/Appreciative_inquiry)、[焦点解决](http://sfwork.com/solutions-focus) --- -“Agile agile Agile agile agile agile Agile agile.”  +“敏捷 敏捷 敏捷 敏捷 敏捷 敏捷 敏捷 敏捷。” -A mantra? Not really, though it may induce an altered state of consciousness.  +咒语?并不是,虽然它可能会诱导意识状态的改变。  -“The answer to the ultimate question of life, the universe and everything?” (Douglas Adams, The Hitchhiker’s Guide to the Galaxy). Maybe, depending on who you ask. +“生命、宇宙和一切问题的终极答案?(Douglas Adams,《银河系漫游指南》)。也许,这取决于你问谁。 -These are homonyms. Words that look and sound the same but have different meanings. Like this grammatically correct sentence composed of three very different words: “Buffalo buffalo Buffalo buffalo buffalo buffalo Buffalo buffalo” (Dmitri Borgmann, Beyond Language: Adventures in Word and Thought). +这些都是同音异义词。看起来和听起来一样,但是意思不同的单词。就像这个由三个意思完全不同的单词组成的语法正确的句子:[Buffalo buffalo Buffalo buffalo buffalo buffalo Buffalo buffalo](https://zh.wikipedia.org/wiki/Buffalo_buffalo_Buffalo_buffalo_buffalo_buffalo_Buffalo_buffalo)[(Dmitri Borgmann, Beyond Language: Adventures in Word and Thought)](https://en.wikipedia.org/wiki/Beyond_Language)。(译者注:中文表述为“水牛城中某些被其他美洲野牛所恐吓的美洲野牛,又去恐吓了另一些美洲野牛。”) -The risk in over-homonymization is that words begin to mean anything and everything until they mean nothing. This is a psychological phenomenon known as “semantic satiation.” Coined by psychologist Leon James, “semantic satiation” is a form of [mental fatigue](http://mentalfloss.com/article/71855/why-does-word-sometimes-lose-all-meaning): +过度同音化的风险是,词语开始意味着任何东西,直到它们变得毫无意义。这是一种被称为“语义饱和”的心理现象。由心理学家 Leon James 创造,“语义饱和”是[精神疲劳](http://mentalfloss.com/article/71855/why-does-word-sometimes-lose-all-meaning)的一种形式: -> It’s called reactive inhibition: When a brain cell fires, it takes more energy to fire the second time, and still more the third time, and finally the fourth time it won’t even respond unless you wait a few seconds…if you repeat a word, the meaning in the word keeps being repeated, and then it becomes refractory, or more resistant to being elicited again and again.   +> 这叫做反应性抑制:当一个脑细胞激活时,第二次激活需要更多能量,第三次更多,最后第四次它甚至不会响应,除非你等待几秒钟……如果你重复一个词,这个词的意思一直在重复,然后变得难以控制,或者更能抵抗一次又一次地被引出。 -Today “Agile” means anything and everything. Increasingly, it means nothing. Many organizations are “Agile” fatigued and refractory, or resistant, to “Agile agile Agile agile agile agile Agile agile.”  +今天,“敏捷”意味着一切。逐渐地,它失去了所有意义。许多组织对敏捷感到厌倦,不受控制,或抵制:“敏捷 敏捷 敏捷 敏捷 敏捷 敏捷 敏捷 敏捷。” -It gets worse. “When words lose their meaning, people lose their freedom” (Confucius). In some organizations, “Agile” has come to mean “command-and-control management.” Kent Beck voices the dismay of many who know better: +它变得更糟。子曰:“言之无文,行之不远。”在一些组织中,“敏捷”已经意味着“命令和控制管理”。Kent Beck 道出了许多知情人的沮丧: -> I was in South Africa at Agile Africa and somebody came up to me and said, ‘We want to do software development but we just can’t stand all this ceremony and this Agile stuff. We just want to write some programs.’ Tears came into my eyes…How can it be that we’re right back where we were twenty years ago? (Personal Correspondence, Quoted with Permission).  +>我在南非参加 Agile Africa 的时候,有人走过来对我说,“我们想做软件开发,但是我们不能忍受所有的繁文缛节和敏捷的规矩。我们只是想写一些程序。”我热泪盈眶……我们怎么又回到了 20 年前的水平呢?(私人通信,经允许引用)。  -That’s a good and important question. And raises other important questions, like “Where do we go from here?” Ron Jeffries’ recently posed one very real [possibility for consideration](https://ronjeffries.com/articles/018-01ff/abandon-1/):  +这是个很重要的好问题。也引出了另一个重要的问题,比如:“我们该何去何从?” Ron Jeffries 最近提出了一个非常真实的[考虑的可能性](https://ronjeffries.com/articles/018-01ff/abandon-1/): -> It’s time to try something new, and here it is: Developers should abandon ‘Agile’…I really am coming to think that software developers of all stripes should have no adherence to any ‘Agile’ method of any kind. As those methods manifest on the ground, they are far too commonly the enemy of good software development rather than its friend.  +> 是时候尝试一些新的东西了,现在就是:开发人员应该放弃“敏捷”……我真的开始认为所有类型的软件开发人员都不应该坚持任何类型的“敏捷”方法。正如这些方法在实际中所表现的那样,它们通常是优秀软件开发的敌人而非朋友。  -Wherever we go from here, let us begin by conceding that many of us Agilists are part of the problem. As Pogo famously said to Porkypine, “We have met the enemy and he is us” (Walt Kelly, Pogo). Martin Fowler put it [this way](https://martinfowler.com/articles/agile-aus-2018.html) at Agile Australia 2018:  +无论我们从何而来,首先,我们要承认,我们中的许多敏捷者都是问题的一部分。正如 Pogo 对 Porkypine 说的一句名言,“我们遇到的敌人正是我们自身。”(Walt Kelly, Pogo)。Martin Fowler 在 Agile Australia 2018 上[这样](https://martinfowler.com/articles/agile-aus-2018.html)说到:  -> …the Agile Industrial Complex imposing methods upon people…is an absolute travesty. I was gonna say ‘tragedy’, but I think ‘travesty’ is the better word because in the end there is no one-size-fits-all in software development. Even the agile advocates wouldn't say that agile is necessarily the best thing to use everywhere. The point is, the team doing work decides how to do it. That is a fundamental agile principle. That even means if the team doesn't want to work in an agile way, then agile probably isn't appropriate in that context, and [not using agile] is the most agile way they can do things in some kind of strangely twisted world of logic. So that's the first problem: the Agile Industrial Complex and this imposition of one-best-way of doing things. That's something we must fight against.  +> ......将敏捷工业综合体的方法强加于人,绝对是一种戏仿。我本来想说“悲剧”,但是我认为“戏仿”这个词更好,因为在软件开发中没有放之四海而皆准的东西。即使是敏捷的拥护者也不会说敏捷一定是在任何地方都能使用的最好的方法。关键是,团队决定了如何去做。这是一个基本的敏捷原则。这甚至意味着,如果团队不想以敏捷的方式工作,那么敏捷在这种情况下可能是不合适的,并且 **不使用敏捷** 是他们在这光怪陆离的逻辑世界中能够做到最敏捷的方式。所以这就是第一个问题:敏捷的工业综合体和这种强加于人的最佳做事方式。这是我们必须反对的。 -The Agile Industrial Complex. Dark Agile. Faux Agile. Zombie Agile. And it gets even worse. So says an organizational psychologist friend: +敏捷工业综合体。黑暗敏捷。人造敏捷。僵尸敏捷。使其变得更糟。组织心理学家朋友说: -> Agile is a virus, spreading across the enterprise. And you shouldn’t be surprised by the growing resistance. Because that’s what antibodies naturally do when an antigen invades. (Personal Correspondence) +> 敏捷是一种病毒,正在企业中蔓延。你不应该对不断增长的阻力感到惊讶。因为当抗原侵入时,抗体自然会这样做。(个人通信) -Huh? +啊哈? -> That’s what it feels like: an invasion. Because your business transformation ‘experts’ know surprisingly little about organizational dynamics and the psychology of change. One blatant instance: do you realize how much resistance you instantly create—on multiple levels—when you declare somebody a “Master?” Especially when the only mastery they have is of a two-day training event! (ibid.) +> 这就是它给人的感觉:侵略。因为你的业务转型“专家”对组织动力学和变化心理学知之甚少。一个明显的例子是:当你宣布某人为“大师”时,你意识到你会立即遇到多大的阻力吗?尤其是当他们唯一精通的是为期两天的训练时!(出处同上) -Oh. I dared not tell her the “Coach” is also declared after a two-day training event. I recently heard one of these “coaches” asked, “It must take a very good project manager to make Agile work?”  -“Yes, a top-notch project manager, iteration manager, Scrum Master, whatever you want to call them, who speaks softly but carries a very big stick!”  -Tears came into my eyes.  +哦。我不敢告诉她,“教练”也是经过两天的训练后宣布的。我最近听到其中一位“教练”问道,“必须有一个非常好的项目经理才能进行敏捷工作吗?” +“是的,一个一流的项目经理、迭代经理、Scrum 大师,不管你怎么称呼他们,他说话温柔,但手握一根大棒!” +我已热泪盈眶。  -One of my clients, after exploring the vast certification landscape, created his own. And dozens of Scrum Masters and Product Owners proudly display it in their work spaces: Agile Yahoo.  +我的一位客户在探索了广阔的认证领域后,创建了自己的认证系统。数十位 Scrum 大师和产品负责人自豪地在他们的工作空间中展示它:雅虎敏捷。  -Where do we go from here?  +我们该何去何从?  -## Domestic Policy—Inside the Agile World  +## 对内政策 —— 敏捷世界中  -Domestic policy is a broad and comprehensive strategy, or a specific plan, or even a simple principle for managing affairs at home.  +对内政策是一项广泛而全面的战略,是一项具体的计划,甚至是一项管理内部事务的简单原则。  -In the era of Agile expansion—business transformation—let us, first, clarify what we mean by “Agile agile agile.”  +在这个敏捷扩展的业务转换时代,首先让我们澄清一下“敏捷 敏捷 敏捷”的含义。  -To state what should be obvious, here is a simple principle to live by: Anything “Agile” must explicitly or implicitly reference the four values and 12 Principles of the Agile Manifesto (https://agilemanifesto.org). It must contain Agile “clues.”  +为了说明什么应该是显而易见的,这里有一个简单的原则:任何“敏捷”都必须或隐或显地引用敏捷宣言的[四个价值观和 12 条原则](https://agilemanifesto.org)。它必须包含敏捷的“线索”。  -We must go back to the future, back to the basics, back to the fundamentals. Agile needs a reboot. “Agile” teams should revisit the Manifesto and 12 Principles on a regular basis: What does it mean? How are we doing? How can we continue moving in this direction? +我们必须回到未来,回到基础,回到根本。敏捷需要重启。“敏捷”团队应该定期回顾宣言和 12 条原则:这意味着什么?我们做得怎么样?我们如何才能继续朝着这个方向前进? -Part of what it means is continual pruning of our own “Agile” practices if they are to remain Agile. “Simplicity is essential” (The 12 Principles) is an Agile “clue” and we must drink our own Kool-Aid.  +它的部分含义是,如果我们自己的“敏捷”实践想要保持敏捷,就必须持续调整它们。“简单是必要的”(12 条原则)是一个敏捷的“线索”,我们必须畅饮自己的 Kool-Aid。  -It really is this simple, says [Dave Thomas](https://pragdave.me/blog/2014/03/04/time-to-kill-agile.html): +就是这么简单,[Dave Thomas](https://pragdave.me/blog/2014/03/04/time-to-kill-agile.html) 说: -> Find out where you are. Take a small step towards your goal. Adjust your understanding based on what you learned. Repeat.  +> 找出你在哪里。朝着你的目标迈出一小步。根据所学内容调整你的理解并重复。 -Similarly, Alistair Cockburn’s [Heart of Agile](https://heartofagile.com/) is an agnostic approach based on a simple framework: Collaborate, Deliver, Reflect, and Improve. Joshua Kerievsky’s [Modern Agile](https://heartofagile.com) is based on four simple principles: Make People Awesome, Make Safety a Prerequisite, Experiment and Learn Rapidly, and Deliver Value Continuously.  +类似地,Alistair Cockburn 的 [Heart of Agile](https://heartofagile.com/) 是一种基于简单框架的不可知论方法:协作、交付、反映和改进。Joshua Kerievsky 的 [Modern Agile](http://modernagile.org/) 基于四个简单的原则:让人变得优秀,把安全作为先决条件,快速地试验和学习,持续地传递价值。  -## Foreign Policy—Outside the Agile World +## 对外政策 —— 敏捷世界外 -Foreign policy is a broad and comprehensive strategy, or a specific plan, or even a simple principle for managing affairs abroad.  +对外政策是一项广泛而全面的战略,是一项具体的计划,甚至是一项管理外部事务的简单原则。 -In the era of Agile expansion—business transformation—let us, second, clarify what we intend by “Agile agile agile.”  +在这个敏捷扩展的业务转换时代,其次让我们阐明“敏捷 敏捷 敏捷”的意图。  -When people groups, such as Agilists, set sail for other lands, cultures inevitably clash.   +当像敏捷者这样的人群开拓其他土地时,文化冲突是不可避免的。  -Early Agile expeditions were characterized by gunboat diplomacy. Our conquest of Project Management, for instance, is nearly complete. -Now we are encountering strange new lands such as Human Resources and meeting people groups called Organizational Psychologists who have bigger certifications than we do. +早期敏捷探险的特点是炮艇外交。例如,我们对项目管理的征服已经接近完。 +现在,我们遇到了一些奇怪的新领域,比如人力资源和组织心理学家,那些资历比我们高的人。 -What is our diplomatic policy? Do we consider ourselves raiders or traders?  +我们的对外政策是什么?我们认为自己是侵略者还是商人?  -Let us beware of a naïve—and ultimately self-defeating—colonialism that presumes we are superior and the natives need to be enculturated for their own good—and our profit.   +让我们警惕一种天真的、最终会自我失败的殖民主义,这种殖民主义假定我们是优越的,土著人需要为他们自己的利益和我们的利益而被文化熏陶。  -Let us beware, conversely, our own assimilation, like the once fearsome Vikings who disappeared into the mists of legend. For example, I am part of a growing movement of Agilists around the world integrating Agile with Positive Psychology, Appreciative Inquiry, and Solution Focused Brief Therapy—see my article on Solution Focused Agile (http://sfio.org/the-journal/interaction-vol-10-no-2-january-2019/page-5/)).  At the same time, an increasing number of these “Agilists” are dropping “Agile” altogether as they are fully assimilated into other worlds.  +相反,让我们警惕我们自己的同化,就像曾经可怕的维京人消失在传说的迷雾中一样。例如,我是世界各地越来越多的敏捷学家的一员,他们将敏捷与积极心理学、欣赏式探究和以焦点解决治疗相结合 —— 参见我的[焦点解决敏捷](http://sfio.org/journal/interaction-vol-10-no-2-janu-2019/page-5/)这篇文章。与此同时,越来越多的“敏捷者”完全放弃了“敏捷”,因为他们已经完全融入了其他世界。  -Our foreign policy across the enterprise is to work towards not a melting pot but a mixed salad.  +我们整个企业的对外政策不是朝一个大熔炉而是一份沙拉的方向努力。  -A simple Conflict Resolution Matrix illustrates this approach (adapted from [here](http://www.cpp.com/en/tkiproducts.aspx?pc=62)). Our stance is neither Competing (Agile wins) nor Accommodating (Agile loses) but Collaborating (the business wins). +一个简单的冲突解决矩阵说明了这种方法(从[这里](http://www.cpp.com/en/tkiproducts.aspx?pc=62)改编而来)。我们的立场不是竞争(敏捷赢)也不是妥协(敏捷输),而是协作(业务赢)。 ![](https://res.infoq.com/articles/agile-agile-blah-blah/en/resources/agile%201-1560257874319.png) -This is an example of the Medici Effect at work. Frans Johansson’s 2006 book, The Medici Effect, was a transformative influence on my thinking. The Medici Effect, named after a 14th century Italian family that sparked The European Renaissance, refers to the breakthrough thinking and disruptive innovation that often bursts out of the big bang collision at the intersection of diverse disciplines, cultures, and industries. This idea resonated with me because I had been conducting big bang experiments since I was a kid with a chemistry set.  +这是美第奇效应的一个例子。Frans Johansson 2006 年出版的《美第奇效应》对我的思维产生了颠覆性的影响。美第奇效应以一个在 14 世纪引发欧洲文艺复兴的意大利家族命名,指的是在不同学科、文化和行业的交叉领域发生的“大爆炸”式碰撞中迸发出来的突破性思维和颠覆性创新。这个想法引起了我的共鸣,因为我从小就在做大爆炸实验。  -The Medici Effect answers a question that I am asked occasionally: Why do I rarely attend Agile events? Agile community is important. But The Medici Effect challenged me to continuously push beyond the boundaries of who and what I already know. And I quickly discovered that, for me, enlightenment and breakthroughs are more often sparked by interaction with military officers, religious leaders, poets, philosophers, biologists, and psychologists. Much of my life’s work has been connecting the dots between these related, sometimes unrelated, disciplines and experimenting with new and different ways of working.  +美第奇效应回答了我偶尔被问到的一个问题:为什么我很少参加敏捷活动?敏捷社区很重要。但是美第奇效应让我不断地超越我对人和事的认知界限。我很快发现,对我来说,启迪和突破更多的是由与军事家、宗教领袖、诗人、哲学家、生物学家和心理学家的互动所激发的。我一生的大部分工作都是把这些相关的,有时是不相关的学科之间的点连接起来,并尝试新的不同的工作方式。  -## Conclusion +## 总结 -Inter-disciplinary research, principles, and practices are the future of Agile. And this makes it even more important that we stay connected to our roots as long as we continue to use the name “Agile.” No more “Agile Agile Agile Blah Blah Blah” please.  +跨学科研究、原则和实践是敏捷的未来。这使得我们与我们的根本保持联系变得更加重要,只要我们继续使用“敏捷”这个名字。请不要再说“敏捷 敏捷 敏捷”之类的话。  > 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 From 703437d6b75d9586420e61eeaf323e521880c4a9 Mon Sep 17 00:00:00 2001 From: sun <776766759@qq.com> Date: Tue, 16 Jul 2019 13:42:08 +0800 Subject: [PATCH 43/68] Create frontend-vs-backend-which-one-is-right-for-you.md (#6148) --- ...d-vs-backend-which-one-is-right-for-you.md | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 TODO1/frontend-vs-backend-which-one-is-right-for-you.md diff --git a/TODO1/frontend-vs-backend-which-one-is-right-for-you.md b/TODO1/frontend-vs-backend-which-one-is-right-for-you.md new file mode 100644 index 00000000000..bd78bd06e9f --- /dev/null +++ b/TODO1/frontend-vs-backend-which-one-is-right-for-you.md @@ -0,0 +1,105 @@ +> * 原文地址:[Frontend vs Backend: Which One Is Right For You?](https://dev.to/molly_struve/frontend-vs-backend-which-one-is-right-for-you-5gjg) +> * 原文作者:[Molly Struve](https://dev.to/molly_struve) +> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) +> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/frontend-vs-backend-which-one-is-right-for-you.md](https://github.com/xitu/gold-miner/blob/master/TODO1/frontend-vs-backend-which-one-is-right-for-you.md) +> * 译者: +> * 校对者: + +# Frontend vs Backend: Which One Is Right For You? + +![](https://res.cloudinary.com/practicaldev/image/fetch/s--sQXuMr9C--/c_imagga_scale,f_auto,fl_progressive,h_420,q_auto,w_1000/https://thepracticaldev.s3.amazonaws.com/i/xtuhivk785yvj2pden2g.png) + +I have been asked many times by new developers what should I study and focus on when I am learning to code? Asking that question is the equivalent of a med student asking what area they should specialize in. There is simply no one size fits all answer. However, I would like to give some guidance and offer some of my own thoughts on the topic. Hopefully, if you are at the start of your career this post will give you a few things to think about. + +## Definitions + +The first internal debate that usually arises when starting in software is where should I focus, the frontend or the backend? Before we dive into the characteristics of each specialty lets first define them. + +### Frontend + +> refers to the presentation layer of a website and how that interacts with the data from the backend. Think HTML, CSS, Javascript, Angular, etc. + +[![](https://res.cloudinary.com/practicaldev/image/fetch/s--rYiDNsAL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/e0vm7fc5bzuqxuhmt80f.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--rYiDNsAL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/e0vm7fc5bzuqxuhmt80f.png) + +### Backend + +> refers to the data processing layer of an application. This is the layer that talks to the database and determine's what information gets sent to the frontend to be displayed. Think Ruby, Rails, Python, Java, etc. + +[![](https://res.cloudinary.com/practicaldev/image/fetch/s--K81Tz4o2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/bqj0p9v42macnqlis6ow.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--K81Tz4o2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/bqj0p9v42macnqlis6ow.png) + +Ok, now we know what they are, but how do you choose which one you want to work with for your career? Honestly, it comes down to personal preference and why you choose to become a dev in the first place. + +## Job Satisfaction + +If you choose to become a dev because you wanted career satisfaction and you wanted to do something you enjoy, then my advice is to do both when you start. Dabble in the frontend and the backend, that way you can get a feel for what you enjoy more. Will it be more work? Definitely, but you will greatly increase your chances of finding a part of the stack you enjoy working with. + +Within the frontend and backend ecosystems, there are still many specialties you can branch off and do which can be overwhelming. When you are starting, try to get a feel for the basics and don't worry too much about diving all the way in. Test out the water and see if either one really grabs you when you work with it. However, be aware when you are starting out that no matter where you begin it is going to be tough at first. I would say give yourself a year or two of working across the entire stack before you decide where you would like to focus. That will give you enough time to get over the initial "Wow, this sucks because it is hard" hump and into the time when you can really assess if it is a technology you enjoy working with. + +While everyone has different tastes, you might find it interesting to see what languages and technologies other devs enjoy working with. The 2019 StackOverflow Survey looked at [what languages were most loved](https://insights.stackoverflow.com/survey/2019#technology-_-most-loved-dreaded-and-wanted-languages). + +[![](https://res.cloudinary.com/practicaldev/image/fetch/s--Jzs_nPT6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/85q0iiaxn4q1gfx9w2ny.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--Jzs_nPT6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/85q0iiaxn4q1gfx9w2ny.png) + +Another advantage to working across the entire stack to start is that you get a feel for how everything works together. This can be immensely useful no matter where you decide to focus on in the future. If you have knowledge about how the other half works that will only allow you to create better code and interfaces within your specialty. + +Lastly, when working across the entire stack you might decide you don't want to choose! You may want to work across the entire stack and be a fullstack engineer for your career. That is completely valid as well! + +## Salary/Stability + +If your motivation for moving to a dev career was for the salary and stability it offers, then studying both might be a waste of your time. If you want to get into a career as fast as possible then do some research for the area you want to work in. Find out what the trends are in salary for frontend vs backend. Also, try to find out which type of dev is in the most demand. + +I don't claim to know whether the frontend or backend is paid more, but there are some surveys out there that have tried to answer this question. Once again, we can look at the 2019 StackOverflow Survey which broke down [salary of devs by type](https://insights.stackoverflow.com/survey/2019#work-_-salary-by-developer-type). + +### Global + +1. Fullstack $57k +2. Backend $56k +3. Frontend $52k + +### United States + +1. Backend $116k +2. Fullstack $110k +3. Frontend $103k + +In addition, it broke down salaries [based on technology](https://insights.stackoverflow.com/survey/2019#top-paying-technologies). Here is a sampling from each of those surveys. + +### Global + +* Clojure $90k +* Go $80k +* Python $63k +* Swift $59k +* JavaScript $56k +* HTML/CSS $55k + +### United States + +* Scala $143k +* Clojure $139k +* Go $136k +* Swift $120k +* Python $116k +* JavaScript $110k +* HTML/CSS $105k + +It is important to note that these salaries and trends may be different depending on where you work and whether you are looking for a remote job or not. Definitely, do your research. It could be as simple as looking at job boards and running a search for backend and frontend technologies and seeing how many results are returned. + +## Why I Choose the Backend + +I figured I would throw in here why I ended up choosing the backend in hopes that it might inform others when making their decision. I was seeking job satisfaction when I made the career switch to being a dev and decided to start out working across the entire stack. I worked as a full-stack dev for 3 years before I started to really shift towards the backend. What drew me to the backend was the cleanliness of Ruby. Javascript and frontend languages have always felt less organized to me. I also thrive on optimizing code performance. I love trying to find ways to make things run better and faster. The backend seemed to give me more opportunities to do that. + +Finally, I am not a very visual or artistic person. Some people can look at a webpage and figure out how to lay it out and where everything should go. I never was good at that so the backend felt more natural and comfortable for me. + +If you want more insight into other's opinions, check out this [CodeNewbie Chat](https://wakelet.com/wake/7d71f467-89ba-49cb-a196-4e32657369ac) which discusses Frontend vs Backend web development. You can also check out the dev.to thread I started Tuesday asking people what part of the stack they choose to work in and why. + +[Frontend vs Backend, which do you prefer and why?](https://dev.to/molly_struve/frontend-vs-backend-which-do-you-prefer-and-why-5a9e) + +## Nothing Is Forever + +No matter what you decide to focus on, know that nothing is forever. If you go down one path and decide that it was the wrong one, you can always switch. One of the great things about software engineering is that it all fits together. Knowing a lot about one area will only help you learn and be better at another. + +> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 + +--- + +> [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[区块链](https://github.com/xitu/gold-miner#区块链)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计)、[人工智能](https://github.com/xitu/gold-miner#人工智能)等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。 From aa3996945fc64b85c09fb2e8f64663287333c530 Mon Sep 17 00:00:00 2001 From: LeviDing Date: Tue, 16 Jul 2019 15:14:38 +0800 Subject: [PATCH 44/68] Create javascript-knowledge-reading-source-code.md --- ...avascript-knowledge-reading-source-code.md | 160 ++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 TODO1/javascript-knowledge-reading-source-code.md diff --git a/TODO1/javascript-knowledge-reading-source-code.md b/TODO1/javascript-knowledge-reading-source-code.md new file mode 100644 index 00000000000..3288c768f42 --- /dev/null +++ b/TODO1/javascript-knowledge-reading-source-code.md @@ -0,0 +1,160 @@ +> * 原文地址:[Improve Your JavaScript Knowledge By Reading Source Code](https://www.smashingmagazine.com/2019/07/javascript-knowledge-reading-source-code/) +> * 原文作者:[Carl Mungazi](https://www.smashingmagazine.com/author/carl-mungazi/) +> * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) +> * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/javascript-knowledge-reading-source-code.md](https://github.com/xitu/gold-miner/blob/master/TODO1/javascript-knowledge-reading-source-code.md) +> * 译者: +> * 校对者: + +# Improve Your JavaScript Knowledge By Reading Source Code + +Quick summary: When you are still early on in your programming career, digging into the source code of open source libraries and frameworks can be a daunting endeavor. In this article, Carl Mungazi shares how he got over his fear and began using source code to improve his knowledge and skills. He also uses Redux to demonstrate how he approaches breaking down a library. + +![](https://d33wubrfki0l68.cloudfront.net/71009a81bc97d80c37de78445977640ee373fb75/34b9c/images/drop-caps/d.svg) ![](https://d33wubrfki0l68.cloudfront.net/f27253afa5fd8465fac5d2cab0c1686450acbdd6/522b6/images/drop-caps/character-10.svg) + +Do you remember the first time you dug deep into the source code of a library or framework you use frequently? For me, that moment came during my first job as a frontend developer three years ago. + +We had just finished rewriting an internal legacy framework we used to create e-learning courses. At the beginning of the rewrite, we had spent time investigating a number of different solutions including Mithril, Inferno, Angular, React, Aurelia, Vue, and Polymer. As I was very much a beginner (I had just switched from journalism to web development), I remember feeling intimidated by the complexity of each framework and not understanding how each one worked. + +My understanding grew when I began investigating our chosen framework, Mithril, in greater depth. Since then, my knowledge of JavaScript — and programming in general — has been greatly helped by the hours I have spent digging deep into the guts of the libraries I use daily either at work or in my own projects. In this post, I will share some of the ways you can take your favorite library or framework and use it as an educational tool. + +[![The source code for Mithril’s hyperscript function](https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_auto/w_400/https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/a94d53ac-c580-4a50-846d-74d997c484d9/2-improve-your-javascript-knowledge-by-reading-source-code.png)](https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/a94d53ac-c580-4a50-846d-74d997c484d9/2-improve-your-javascript-knowledge-by-reading-source-code.png) + +My first introduction to reading code was via Mithril’s hyperscript function. ([Large preview](https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/a94d53ac-c580-4a50-846d-74d997c484d9/2-improve-your-javascript-knowledge-by-reading-source-code.png)) + +### The Benefits Of Reading Source Code + +One of the major benefits of reading source code is the number of things you can learn. When I first looked into Mithril’s codebase, I had a vague idea of what the virtual DOM was. When I finished, I came away with the knowledge that the virtual DOM is a technique which involves creating a tree of objects that describe what your user interface should look like. That tree is then turned into DOM elements using DOM APIs such as `document.createElement`. Updates are performed by creating a new tree describing the future state of the user interface and then comparing it with objects from the old tree. + +I had read about all of this in various articles and tutorials, and whilst it was helpful, being able to observe it at work in the context of an application we had shipped was very illuminating for me. It also taught me which questions to ask when comparing different frameworks. Instead of looking at GitHub stars, for example, I now knew to ask questions such as, “How does the way each framework performs updates affect performance and the user experience?” + +Another benefit is an increase in your appreciation and understanding of good application architecture. Whilst most open-source projects generally follow the same structure with their repositories, each of them contains differences. Mithril’s structure is pretty flat and if you are familiar with its API, you can make educated guesses about the code in folders such as `render`, `router` and `request`. On the other hand, React’s structure reflects its new architecture. The maintainers have separated the module responsible for UI updates (`react-reconciler`) from the module responsible for rendering DOM elements (`react-dom`). + +One of the benefits of this is that it is now easier for developers to write their own [custom renderers](https://github.com/chentsulin/awesome-react-renderer) by hooking into the `react-reconciler` package. Parcel, a module bundler I have been studying recently, also has a `packages` folder like React. The key module is named `parcel-bundler` and it contains the code responsible for creating bundles, spinning up the hot module server and the command-line tool. + +[![The section of the JavaScript specification which explains how Object.prototype.toString works](https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_auto/w_400/https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/6777ea35-ee97-40c4-a0b8-5b4c2455f733/1-improve-your-javascript-knowledge-by-reading-source-code.png)](https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/6777ea35-ee97-40c4-a0b8-5b4c2455f733/1-improve-your-javascript-knowledge-by-reading-source-code.png) + +It will not be long before the source code you are reading leads you to the JavaScript specification. ([Large preview](https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/6777ea35-ee97-40c4-a0b8-5b4c2455f733/1-improve-your-javascript-knowledge-by-reading-source-code.png)) + +Yet another benefit — which came as a welcome surprise to me — is you become more comfortable reading the official JavaScript specification which defines how the language works. The first time I read the spec was when I was investigating the difference between `throw Error` and `throw new Error` (spoiler alert — there is [none](http://www.ecma-international.org/ecma-262/7.0/#sec-error-constructor)). I looked into this because I noticed that Mithril used `throw Error` in the implementation of its `m` function and I wondered if there was a benefit to using it over `throw new Error`. Since then, I have also learnt that the logical operators `&&` and `||` [do not necessarily return booleans](https://tc39.es/ecma262/#prod-LogicalORExpression), found the [rules](http://www.ecma-international.org/ecma-262/#sec-abstract-equality-comparison) which govern how the `==` equality operator coerces values and the [reason](http://www.ecma-international.org/ecma-262/#sec-object.prototype.tostring) `Object.prototype.toString.call({})` returns `'[object Object]'`. + +### Techniques For Reading Source Code + +There are many ways of approaching source code. I have found the easiest way to start is by selecting a method from your chosen library and documenting what happens when you call it. Do not document every single step but try to identify its overall flow and structure. + +I did this recently with `ReactDOM.render` and consequently learned a lot about React Fiber and some of the reasons behind its implementation. Thankfully, as React is a popular framework, I came across a lot of articles written by other developers on the same issue and this sped up the process. + +This deep dive also introduced me to the concepts of [co-operative scheduling](https://developer.mozilla.org/en-US/docs/Web/API/Background_Tasks_API), the `[window.requestIdleCallback](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback)` method and a [real world example of linked lists](https://github.com/facebook/react/blob/v16.7.0/packages/react-reconciler/src/ReactUpdateQueue.js#L10) (React handles updates by putting them in a queue which is a linked list of prioritised updates). When doing this, it is advisable to create a very basic application using the library. This makes it easier when debugging because you do not have to deal with the stack traces caused by other libraries. + +If I am not doing an in-depth review, I will open up the `/node_modules` folder in a project I am working on or I will go to the GitHub repository. This usually happens when I come across a bug or interesting feature. When reading code on GitHub, make sure you are reading from the latest version. You can view the code from commits with the latest version tag by clicking the button used to change branches and select “tags”. Libraries and frameworks are forever undergoing changes so you do not want to learn about something which may be dropped in the next version. + +Another less involved way of reading source code is what I like to call the ‘cursory glance’ method. Early on when I started reading code, I installed **express.js**, opened its `/node_modules` folder and went through its dependencies. If the `README` did not provide me with a satisfactory explanation, I read the source. Doing this led me to these interesting findings: + +* Express depends on two modules which both merge objects but do so in very different ways. `merge-descriptors` only adds properties directly found directly on the source object and it also merges non-enumerable properties whilst `utils-merge` only iterates over an object’s enumerable properties as well as those found in its prototype chain. `merge-descriptors` uses `Object.getOwnPropertyNames()` and `Object.getOwnPropertyDescriptor()` whilst `utils-merge` uses `for..in`; +* The `setprototypeof` module provides a cross platform way of setting the prototype of an instantiated object; +* `escape-html` is a 78-line module for escaping a string of content so it can be interpolated in HTML content. + +Whilst the findings are not likely to be useful immediately, having a general understanding of the dependencies used by your library or framework is useful. + +When it comes to debugging front-end code, your browser’s debugging tools are your best friend. Among other things, they allow you to stop the program at any time and inspect its state, skip a function’s execution or step into or out of it. Sometimes this will not be immediately possible because the code has been minified. I tend to unminify it and copy the unminified code into the relevant file in the `/node_modules` folder. + +[![The source code for the ReactDOM.render function](https://res.cloudinary.com/indysigner/image/fetch/f_auto,q_auto/w_400/https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/798703fd-8689-40d9-9159-701f1a00f837/3-improve-your-javascript-knowledge-by-reading-source-code.png)](https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/798703fd-8689-40d9-9159-701f1a00f837/3-improve-your-javascript-knowledge-by-reading-source-code.png) + +Approach debugging as you would any other application. Form a hypothesis and then test it. ([Large preview](https://cloud.netlifyusercontent.com/assets/344dbf88-fdf9-42bb-adb4-46f01eedd629/798703fd-8689-40d9-9159-701f1a00f837/3-improve-your-javascript-knowledge-by-reading-source-code.png)) + +### Case Study: Redux’s Connect Function + +React-Redux is a library used to manage the state of React applications. When dealing with popular libraries such as these, I start by searching for articles that have been written about its implementation. In doing so for this case study, I came across this [article](https://blog.isquaredsoftware.com/2018/11/react-redux-history-implementation). This is another good thing about reading source code. The research phase usually leads you to informative articles such as this which only improve your own thinking and understanding. + +`connect` is a React-Redux function which connects React components to an application’s Redux store. How? Well, according to the [docs](https://react-redux.js.org/api/connect), it does the following: + +> “...returns a new, connected component class that wraps the component you passed in.” + +After reading this, I would ask the following questions: + +* Do I know any patterns or concepts in which functions take an input and then return that same input wrapped with additional functionality? +* If I know of any such patterns, how would I implement this based on the explanation given in the docs? + +Usually, the next step would be to create a very basic example app which uses `connect`. However, on this occasion I opted to use the new React app we are building at [Limejump](https://limejump.com/) because I wanted to understand `connect` within the context of an application which will eventually be going into a production environment. + +The component I am focusing on looks like this: + +``` +class MarketContainer extends Component { + // code omitted for brevity +} + +const mapDispatchToProps = dispatch => { + return { + updateSummary: (summary, start, today) => dispatch(updateSummary(summary, start, today)) + } +} + +export default connect(null, mapDispatchToProps)(MarketContainer); +``` + +It is a container component which wraps four smaller connected components. One of the first things you come across in the [file](https://github.com/reduxjs/react-redux/blob/v7.1.0/src/connect/connect.js) which exports `connect` method is this comment: **connect is a facade over connectAdvanced**. Without going far we have our first learning moment: **an opportunity to observe the [facade](http://jargon.js.org/_glossary/FACADE_PATTERN.md) design pattern in action**. At the end of the file we see that `connect` exports an invocation of a function called `createConnect`. Its parameters are a bunch of default values which have been destructured like this: + +``` +export function createConnect({ + connectHOC = connectAdvanced, + mapStateToPropsFactories = defaultMapStateToPropsFactories, + mapDispatchToPropsFactories = defaultMapDispatchToPropsFactories, + mergePropsFactories = defaultMergePropsFactories, + selectorFactory = defaultSelectorFactory +} = {}) +``` + +Again, we come across another learning moment: **exporting invoked functions** and **destructuring default function arguments**. The destructuring part is a learning moment because had the code been written like this: + +``` +export function createConnect({ + connectHOC = connectAdvanced, + mapStateToPropsFactories = defaultMapStateToPropsFactories, + mapDispatchToPropsFactories = defaultMapDispatchToPropsFactories, + mergePropsFactories = defaultMergePropsFactories, + selectorFactory = defaultSelectorFactory +}) +``` + +It would have resulted in this error `Uncaught TypeError: Cannot destructure property 'connectHOC' of 'undefined' or 'null'.` This is because the function has no default argument to fall back on. + +**Note**: **For more on this, you can read David Walsh’s [article](https://davidwalsh.name/destructuring-function-arguments). Some learning moments may seem trivial, depending on your knowledge of the language, and so it might be better to focus on things you have not seen before or need to learn more about.** + +`createConnect` itself does nothing in its function body. It returns a function called `connect`, the one I used here: + +```javascript +export default connect(null, mapDispatchToProps)(MarketContainer) +``` + +It takes four arguments, all optional, and the first three arguments each go through a `[match](https://github.com/reduxjs/react-redux/blob/v7.1.0/src/connect/connect.js#L25)` function which helps define their behaviour according to whether the arguments are present and their value type. Now, because the second argument provided to `match` is one of three functions imported into `connect`, I have to decide which thread to follow. + +There are learning moments with the [proxy function](https://github.com/reduxjs/react-redux/blob/v7.1.0/src/connect/wrapMapToProps.js#L29) used to wrap the first argument to `connect` if those arguments are functions, the `[isPlainObject](https://github.com/reduxjs/react-redux/blob/v7.1.0/src/utils/isPlainObject.js)` utility used to check for plain objects or the `[warning](https://github.com/reduxjs/react-redux/blob/v7.1.0/src/utils/warning.js)` module which reveals how you can set your debugger to [break on all exceptions](https://developers.google.com/web/tools/chrome-devtools/javascript/breakpoints#exceptions). After the match functions, we come to `connectHOC`, the function which takes our React component and connects it to Redux. It is another function invocation which returns `[wrapWithConnect](https://github.com/reduxjs/react-redux/blob/v7.1.0/src/components/connectAdvanced.js#L123)`, the function which actually handles connecting the component to the store. + +Looking at `connectHOC`’s implementation, I can appreciate why it needs `connect` to hide its implementation details. It is the heart of React-Redux and contains logic which does not need to be exposed via `connect`. Even though I will end the deep dive here, had I continued, this would have been the perfect time to consult the reference material I found earlier as it contains an incredibly detailed explanation of the codebase. + +### Summary + +Reading source code is difficult at first but as with anything, it becomes easier with time. The goal is not to understand everything but to come away with a different perspective and new knowledge. The key is to be deliberate about the entire process and intensely curious about everything. + +For example, I found the `isPlainObject` function interesting because it uses this `if (typeof obj !== 'object' || obj === null) return false` to make sure the given argument is a plain object. When I first read its implementation, I wondered why it did not use `Object.prototype.toString.call(opts) !== '[object Object]'`, which is less code and distinguishes between objects and object sub types such as the Date object. However, reading the next line revealed that in the extremely unlikely event that a developer using `connect` returns a Date object, for example, this will be handled by the `Object.getPrototypeOf(obj) === null` check. + +Another bit of intrigue in `isPlainObject` is this code: + +```javascript +while (Object.getPrototypeOf(baseProto) !== null) { + baseProto = Object.getPrototypeOf(baseProto) +} +``` + +Some Google searching led me to [this](https://stackoverflow.com/questions/51722354/the-implementation-of-isplainobject-function-in-redux/51726564#51726564) StackOverflow thread and the Redux [issue](https://github.com/reduxjs/redux/pull/2599#issuecomment-342849867) explaining how that code handles cases such as checking against objects which originate from an iFrame. + +#### Useful Links On Reading Source Code + +* “[How To Reverse Engineer Frameworks](https://blog.angularindepth.com/level-up-your-reverse-engineering-skills-8f910ae10630),” Max Koretskyi, Medium +* “[How To Read Code](https://github.com/aredridel/how-to-read-code/blob/master/how-to-read-code.md),” Aria Stewart, GitHub + +> 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 + +--- + +> [掘金翻译计划](https://github.com/xitu/gold-miner) 是一个翻译优质互联网技术文章的社区,文章来源为 [掘金](https://juejin.im) 上的英文分享文章。内容覆盖 [Android](https://github.com/xitu/gold-miner#android)、[iOS](https://github.com/xitu/gold-miner#ios)、[前端](https://github.com/xitu/gold-miner#前端)、[后端](https://github.com/xitu/gold-miner#后端)、[区块链](https://github.com/xitu/gold-miner#区块链)、[产品](https://github.com/xitu/gold-miner#产品)、[设计](https://github.com/xitu/gold-miner#设计)、[人工智能](https://github.com/xitu/gold-miner#人工智能)等领域,想要查看更多优质译文请持续关注 [掘金翻译计划](https://github.com/xitu/gold-miner)、[官方微博](http://weibo.com/juejinfanyi)、[知乎专栏](https://zhuanlan.zhihu.com/juejinfanyi)。 From d03b7041d3dda16ab2b531450f1fe62306b0a5b8 Mon Sep 17 00:00:00 2001 From: LeviDing Date: Tue, 16 Jul 2019 16:30:15 +0800 Subject: [PATCH 45/68] Update javascript-knowledge-reading-source-code.md --- TODO1/javascript-knowledge-reading-source-code.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/TODO1/javascript-knowledge-reading-source-code.md b/TODO1/javascript-knowledge-reading-source-code.md index 3288c768f42..d94f0557250 100644 --- a/TODO1/javascript-knowledge-reading-source-code.md +++ b/TODO1/javascript-knowledge-reading-source-code.md @@ -9,8 +9,6 @@ Quick summary: When you are still early on in your programming career, digging into the source code of open source libraries and frameworks can be a daunting endeavor. In this article, Carl Mungazi shares how he got over his fear and began using source code to improve his knowledge and skills. He also uses Redux to demonstrate how he approaches breaking down a library. -![](https://d33wubrfki0l68.cloudfront.net/71009a81bc97d80c37de78445977640ee373fb75/34b9c/images/drop-caps/d.svg) ![](https://d33wubrfki0l68.cloudfront.net/f27253afa5fd8465fac5d2cab0c1686450acbdd6/522b6/images/drop-caps/character-10.svg) - Do you remember the first time you dug deep into the source code of a library or framework you use frequently? For me, that moment came during my first job as a frontend developer three years ago. We had just finished rewriting an internal legacy framework we used to create e-learning courses. At the beginning of the rewrite, we had spent time investigating a number of different solutions including Mithril, Inferno, Angular, React, Aurelia, Vue, and Polymer. As I was very much a beginner (I had just switched from journalism to web development), I remember feeling intimidated by the complexity of each framework and not understanding how each one worked. From c78e05110e86832d1aaa21fea9e1f2343a2c0e05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=92=B1=E4=BF=8A=E9=A2=96?= <49943038+Baddyo@users.noreply.github.com> Date: Tue, 16 Jul 2019 16:52:42 +0800 Subject: [PATCH 46/68] =?UTF-8?q?npm=20=E7=9A=84=E7=BB=8F=E6=B5=8E?= =?UTF-8?q?=E9=A3=8E=E4=BA=91=20=E2=80=94=E2=80=94=20=E4=B8=8B=E5=8D=8A?= =?UTF-8?q?=E9=83=A8=E5=88=86=20(#6098)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * npm 的经济风云 —— 下半部分 npm 的经济风云 —— 下半部分 * 第一次修改 感谢 @LanceZhu 的宝贵建议。 * 第二次修改 将”投机者“改为”拉投资的企业“。 * 第三次修改完成 感谢校对者 @MarchYuanx @Leviding --- .../the-economics-of-package-management-2.md | 156 +++++++++--------- 1 file changed, 78 insertions(+), 78 deletions(-) diff --git a/TODO1/the-economics-of-package-management-2.md b/TODO1/the-economics-of-package-management-2.md index c9193d27199..c964bd2a578 100644 --- a/TODO1/the-economics-of-package-management-2.md +++ b/TODO1/the-economics-of-package-management-2.md @@ -2,162 +2,162 @@ > * 原文作者:[ceejbot](https://github.com/ceejbot) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/the-economics-of-package-management-2.md](https://github.com/xitu/gold-miner/blob/master/TODO1/the-economics-of-package-management-2.md) -> * 译者: -> * 校对者: +> * 译者:[Baddyo](https://juejin.im/user/5b0f6d4b6fb9a009e405dda1) +> * 校对者:[LanceZhu](https://github.com/LanceZhu), [MarchYuanx](https://github.com/MarchYuanx) -# The economics of package management - 下半部分 +# npm 的经济风云 —— 下半部分 > 如果你还没读过本系列的上半部分,请先阅读: > -> * [The economics of package management - 上半部分](https://github.com/xitu/gold-miner/blob/master/TODO1/the-economics-of-package-management-1.md) +> * [npm 的经济风云 —— 上半部分](https://github.com/xitu/gold-miner/blob/master/TODO1/the-economics-of-package-management-1.md) -For us, in the Javascript community, our commons includes the language spec itself. That's owned by an organization set up to allow all Javascript's stakeholders to cooperate to design and build it. TC39 has done pretty well by Javascript. +而从 JavaScript 的层面来说,这门语言的规范就是属于我们开发者的公共资源的一部分。它由一个专门成立的组织所拥有,这个组织让所有的 JavaScript 利益相关者齐心协力来设计和构建这门语言。TC39 委员会在这方面功不可没。 -Another thing our commons includes is all our shared code. The source code for Babel, the source code for webpack, the source code for TypeScript, for React, for Angular— these and thousands of other packages make up our commons. The _registry that lists all of this shared code_ is also part of our commons. It’s how we share all that stuff with each other, how we find it. Another thing that’s part of our commons is the set of conventions we've evolved around that -- the ways we agree to name and update the things we share. +所有的开源代码也都是我们的 JavaScript 公共资源的一部分。Babel 的源代码、webpack 的源代码、TypeScript 的源代码,以及 React、Angular —— 成千上万的开源包构成了属于我们的 JavaScript 公共资源。**维护着这些开源包的注册中心**同样也是 JavaScript 公共资源。有了注册中心,我们才能与彼此分享,才能便捷地找到需要的源码。JavaScript 公共资源还包括命名规范、更新方式等一系列开发约定。 -But all of _that_ is wholly owned by a VC-funded private company. +但现在,**所有这些**,完完全全被一家由风投资金赞助的私人公司所掌握。 -Last year at this conference, Ryan Dahl, the inventor of node, came back to make another announcement. He talked about [design mistakes](~~https://tinyclouds.org/jsconf2018.pdf~~) he thought he’d made with node. Here's one. +在去年的这个大会上,Node.js 的发明者 Ryan Dahl 再次登台演讲。他谈到了 Node.js 的[设计误区](https://tinyclouds.org/jsconf2018.pdf)。我在此引用他的一句话。 -> It’s unfortunate that there is a centralized (privately controlled even) repository for modules. --Ryan Dahl +> 模块仓库的中心化(甚至由私人所控制)是一种不幸。—— Ryan Dahl -When I first saw that, I was inclined to argue with Dahl about centralization, but I had to agree with his comment about private control. +当我第一次听到这话,我很想就中心化的概念和 Dahl 辩论一番,但关于他说的私人控制的部分,我不得不信服。 -Let's talk about that for a moment. The list of shared Javascript packages we all made to share with each other is under private control. What does that mean? +那我们就说说私人控制。我们赖以共享 JavaScript 代码的平台被私人掌控。其含义是什么? -Policies for managing that list are controlled by a single company, not by the Javascript community. What gets a package removed? How are disputes among people using package names resolved? This is a controversial topic! Remember left-pad? +npm 的经营策略由一个公司来掌舵,JavaScript 社区对此没有话语权。那删除代码包是出于何种原因?代码包的命名纠纷又该如何解决?这是一个有争议的话题!你们是否还记得 left-pad 事件? -That was an incident where a package that was a dependency of a dependency vanished from the registry suddenly, breaking every CI build that depended on installing babel. Why did left-pad suddenly vanish? Buy me a drink and I'll tell you! But the unpublication policy didn't exist before this fight, and it's both making your life better and worse daily. +该事件是这样的:left-pad 是另一个依赖包用到的依赖包,当它突然被从 npm 中删除,每一个依赖于安装 Babel 的持续集成构建软件(CI build)都崩溃了。那为何 left-pad 会突然被删除呢?请我吃饭我就告诉你!但这种不公开的经营策略此前并不存在,它让我们的生活越来越好,也越来越糟。 -Want package signing? Good luck! That feature doesn't make the company money, so you are unlikely to see it. Perhaps fear of a security incident, or major public outcry would get this done. +想要包签名功能?那可有得盼了!这个功能又不能给公司创收,因此你们不太可能看到它上线。也许只有对安全事故的畏惧,或者公众强烈抗议才能促成这件事。 -Note here that I’m talking about the _package registry_ almost exclusively here. The registry is closed source, and the policies that regulate it are not open to our influence. The npm cli is open source, but it is unimportant. The API is open, for now, and the important building blocks you would need for writing your own cli are all open-source and not under npm’s control. About a third of you use another client to interact with npm, anyway. You can tell the cli doesn't matter because they seek out community participation in it. +请注意,我在这里仅仅是在说**代码包注册中心**。注册中心是闭源的,其经营策略不受我们控制。npm 脚手架工具(cli)倒是开源的,但这不是重点。npm 的 API 暂时还是开源的,编写自己的脚手架所需的重要构建模块也都是开源的,不受 npm 公司的控制。不论如何,你们中有三分之一的人使用的是另外的客户端与 npm 交互。我们可以认为脚手架工具不牵涉其中,因为在这方面,npm 公司需要社区来出力。 -The API is open because the company chooses to allow it to be open. You don't have any control over that. You also can't influence that API and get changes that the ecosystem might benefit from. +API 之所以开源,是因为该公司允许其开源。我们根本无法控制其一分一毫。我们无法对 API 产生影响,更无法做对 JavaScript 生态有益处的改变。 -The management of your commons is opaque to you. It will remain opaque to you until the company that owns it has a financial incentive to change that. You do not know what it's doing with your package data-- you cannot know. You have to trust it. +我们碰不到对自己的资源的管理权。这种隔离状态会一直持续,直到该公司出于经济动机去改变它。你不知道他们会用你的代码包数据做什么 —— 你无从知晓。你只能选择信任这种不透明的管理机制。 -There can be no trust without accountability, and we as the Javascript community have no way to hold npm Inc accountable. +但没有问责制就没有信任,而我们做为 Javascript 社区成员,是没有途径去向 npm 公司问责的。 -Now, when I was there, I could tell myself that I trusted myself and my team, and I knew our motivations were good. This was not a good answer and I was wrong to have relied on it. You had no way to hold _me_ accountable and to test that I was trustworthy. +当我还在 npm 团队中时,我可以告诉自己说,我信任我自己,信任我的团队,我知道我们的动机是为了社区好。而现在,这不是一个令人信服的答案,我一度对此的信赖是错误的。你们没有办法让我负责,也没有办法证明我是值得信赖的。 -Whoops. +溜了溜了。 -So npm Inc is a private entity in control of our commons, and we are not. Does that make it evil? No. It doesn’t. It doesn’t make it good, either. The question of its benevolence is the wrong question to ask. +总之,npm 公司是一家私有性质的实体,是它控制着我们的公共资源,而不是我们开发者。这就一定代表它是十恶不赦的吗?不,也不尽然。但也同样不尽然使其积德行善。问它是否仁慈可算是问错对象了。 -npm is not a benevolent institution. It *cannot* be one. The possibility of it being that ended the day its owner took VC funding instead of putting it into a foundation or some other form of community ownership. That decision turned npm into a *financial instrument*. +npm 公司可不是慈善机构。可能性**为零**。即使有那么一点可能性,也早就在接受风投资金而没有把它交给一个基金会或其他形式的社区所有权的那一刻消亡殆尽。那一决策把 npm 变成了**金融工具**。 -I mean something very specific when I say that. +我说这话,完完全全就是字面意思。 -Financial instruments are monetary contracts between parties. They're widgets that can be created and traded. npm Inc the company— the thing that owns our language ecosystem, the thing that owns the registry of packages, the collection of software packaged as the cli you use every day— that is a thing that might as well be a collection of pork bellies as far as its owners are concerned. They make contracts with each other and trade bits of it around. +所谓金融工具,就是资本方之间的货币合同。它们是可以用来创造和交易的小工具。npm 公司 —— 掌控着 JavaScript 生态和你们每天都在使用的 npm 的角色 —— 在其所有者眼里不过是一堆能换钱的猪肚罢了。他们签一份份合同,做一笔笔生意。 -npm-the-company is a means for turning some money into more money for the people who own it. +npm 公司就是其拥有者用来钱生钱的手段。 -It’s important to keep this in mind as you evaluate all your interactions with it, and as you evaluate all your interactions with the people who represent it. What this means is *look at incentives*. Money turns out to be the incentive for a lot of people in this story. +当你使用 npm 时,当你和 npm 方面的人打交道时,你最好记住 npm 公司的性质。我的意思是**着眼于动机**。在这个故事里,多数人的动机归根结底都是金钱。 -Follow the money. +一切向钱看。 -Companies don’t love you, not even ones that make things you like. +商业公司并不爱你,即使是那些做出了让你爱不释手的产品的公司。 -That phrase is a marketing message, designed to get you to mistake the financial instrument for something that might be your friend. That marketing message has been pretty powerful over the years, hasn't it? You've got a cuddly wombat mascot and awesome stickers. Add a red emoji heart and you're on board, right? +“Build amazing things”,这只是一句广告词,用来让你误认为金融工具是你的朋友。多年来,这句广告词一直非常见效,不是吗?给你一个抱抱熊吉祥物,给你一些好看的贴纸,再加上一颗红心表情符号,你就上钩了,对吧? -npm does not love you. npm cannot love you. npm Inc is a Delaware corporation founded as a financial instrument intended to turn money into more money for a handful of men. +npm 公司并不爱你。它不可能爱你。npm 公司只是特拉华州的一家公司,只是少数人的摇钱树罢了。 -When I initially knew I wanted to start talking about this problem in public, instead of in corners with friends, I thought I’d be fighting an uphill battle. The VC-funded package management company was beloved, and my work was one of the reasons why. I had participated in this whole rise to prominence— I made it possible. I stood on stage in front of you all and parroted that marketing message. What I meant by it was, "I, C J Silverio, feel affection for you all," and that was true. +当我开始决定与其躲在角落和朋友窃窃私语,不如在大庭广众之下振臂高呼时,我就知道我要打一场艰苦的战役。npm 这家风投资金赞助的公司大受欢迎,我的犬马之劳也是原因之一。我参与了它的整个崛起过程 —— 我也贡献了力量。我也曾站在舞台上,面对你们大家,鹦鹉学舌地说出那句广告词。我也曾说“我,C J Silverio,爱你们所有人。”那是我的肺腑之言。 -Now I don't think anybody can stand up in front of you and say "npm loves you" without being yelled at. npm has burned all of its goodwill over the last few months. It didn't have to happen-- it was a choice made by the people who run the company. They made that choice, though, and they doubled-down on it, and here we are. +而现在,我不认为有人在你们面前大言不惭地说出 “npm 爱你们”还能够不被骂。过去几个月,npm 已经耗尽了所有的美誉。其实本不该如此 —— 都是公司运作者的决策导致的。他们做出了决策,而且押下双倍赌注,于是乎,我们走到了这般田地。 -How did that happen? Why did the cuddly wombat get set on fire? Let's move our story forward, shall we? +这一切是怎么发生的?为什么抱抱熊开始黑化?让我们接着讲故事吧,好吗? -We pick up in, oh, I don’t know, 2018. You all have been drinking from the firehose of Javascript packages which have been, for you, free. You do not think about or care about where they come from, or who is paying for the servers. You just type `install` and the packages arrive. +我们从,呃,2018 年这个时间点拾起。你们大家一直从免费的 JavaScript 开源包中受益。但你们从没想过,或者在乎过这些开源包是从哪里来的,是谁在为这些服务买单。你们只需敲出 `install` 命令,你们要的代码包就自动上门了。 -But the bandwidth costs money, and somebody pays for it. +但带宽是要花钱的,总得有人来出这笔钱。 -In this case, npm had been spending the dollars given to it by its venture capital investors to pay for the servers and the bandwidth. And eventually the inevitable consequences of taking VC money started to knock on the door and clear their throats. *Ahem. Remember us? We want our money.* We've established that VC-funded companies are financial instruments. They have to start making that famous 10x return on investment. They have to promise somehow that their owners are going to end up winners. This isn’t always about being profitable, but if you don’t manage to do that you have to tell a persuasive enough story that you can raise more money and keep the scheme going. +就这样,npm 花着风投资本家给的钱来购买服务器和带宽。最终报应不爽,把风投资金纳入囊中时种下的恶果就像堵在门口的债主一样开始敲门,清清嗓子准备连本带利地索要回去。“咳咳,还认识我们吗?把我们的钱还给我们。” 我们终于看清,靠风投资金赞助公司只是赚钱工具罢了。资本家们终会无所不用其极以获取 10 倍收益。他们每做一笔投资都要确保金主是最后的赢家。这种生意并不总是只赚不亏,但如果你不能保证保本,那你得有说服力足够的故事让金主相信你能确保赚钱的机器能一直运转下去。 -There is absolutely nothing wrong with taking VC money inherently, in my opinion. Many ventures have goals well-aligned with the goals of VC money, and not all VC money is shaped alike. Some funds play long games. Funds like Futureshape are explicitly interested in bettering the world. But mostly, VC money is interested in returning the investment. That’s where the incentives point: make money, be sold to new owners for enough money to return the investment ten times over. Grow fast. Go big or go home. +从本质上说,我认为接受风险投资并没有错。很多拉投资的企业的目标都和投资方的目标相匹配,并且并非所有的投资方都是一样的。有些投资方喜欢放长线钓大鱼。像 Futureshape 这样的投资方是很明确想让世界变得更美好。但更多的投资方只看重收益。这就是他们的出发点:赚钱,把羊养肥卖给新主顾,赚个十倍利润。你拿了风投资金,就要迅速增长。要么做大,要么作古。 -Remember that npm Inc’s obligations are to the people who own it, not the people who donated their work toward its dragon hoard of packages. +记住,npm 公司里是金主说了算,而不是那些为开源宝库添砖加瓦的开发者。 -So npm Inc has to make money, or failing that tell a story about making more money so it can raise money to spend on making money. +所以 npm 公司必须要盈利,否则就得忽悠更多的投资来拆东墙补西墙。 -The ex-Yahoo employee who thought that sharing the Yahoo package manager with the world was a pretty neat idea was right about that, but he didn’t have many ideas beyond that, and it turns out running companies is a lot of work. So in 2018 he hired a new CEO to do that work for him, and well, here we go. The first thing this new CEO wanted to do was change npm’s culture— you know, the thing it had exported as its marketing message. The sustainable, compassionate culture was the first thing to go when this new guy arrived. +那位觉得把 ypm 开源出来是个好点子的前 Yahoo 员工,他的想法没错,但他没有深思熟虑过开源之后的发展,事实证明,运营公司可不单单有个好点子就行了,还需要很多血汗。因此在 2018 年,他新雇了一位 CEO 为他运营公司,于是乎,这下可有得折腾了。新 CEO 上任的第一把火就是改变了 npm 的文化 —— 你懂的,就是那套一直做为广告语输出的文化。这位新领导就位后,率先踢开的就是宽容、仁慈的 npm 文化。 -npm’s PR troubles as a result are probably known to you now. We’re a community that enjoys our drama. It's possible that if a saint were in charge, I wouldn't be standing here. But maybe I would be, because even a decent human being in that spot has a big problem to solve. +结果搞得 npm 的公关问题路人皆知。我们真是一个爱看热闹的社区。如果管事的是一位圣人,那可能我也不会站在这里。但可能我还是会,因为即使一位正派的领导者处在这种位置,也有很大的问题急需解决。 -Here’s the problem. +这就是问题所在。 -The public registry — the place where all your packages are indexed and stored— that’s the part you care about. That’s the part that is an enormous drain on npm’s budget _and_ simultaneously its value to investors. It controls all Javascript development, because all Javascript development proxies through it willingly. That data — the data gathered from your usage of it— is the valuable part. Every package-lock file npm has ever seen is sitting in an s3 bucket somewhere, chock-full of interesting data nuggets about what you've been up to. It’s also a perverse incentive influencing API design. The company has no incentive to reduce the number of times a client has to phone home when installing, because those calls homeward generate valuable data. +公开的注册中心 —— 所有开源包被索引和存储的地方 —— 是大家所关心的部分。这个注册中心极大地消耗着 npm 的预算,**同时**,这也是投资方觉得有利可图的地方。它控制着所有 JavaScript 开发项目,因为所有 JavaScript 开发项目都自愿以其为代理。数据 —— 用户的使用数据 —— 也是有利可图的地方。每一个 package-lock 文件都存放在亚马逊云存储中的某处,满载着关于用户偏好的有趣数据块。这也是影响 API 设计的强有力因素。npm 公司没理由降低客户端在安装代码包时向服务器发出请求的次数,因为这些请求都能产生有价值的数据。 -The registry is a drain on npm. The work of keeping up with continuous near-exponential growth is itself continuous. During my time there this work occupied most of a very tiny engineering team. Why was the team tiny? Well, npm had no money because it did a pretty bad job of telling its story to new investors and raising more money. Or the story it had to tell was just bad and potential investors knew that. I’m not sure which it was. But either way, npm had enough money to keep the free Javascript flowing to you, but not to make decent products to sell. Eventually the VC bill was going to come due. Eventually they’d have to show growth or die. +注册中心是 npm 的负担。想要跟上持续的、近乎指数级的增长,就要付出同等的工作量。我在 npm 任职时,这种繁重的工作占用了小小的开发团队几乎全部的时间。为何开发团队这么小呢?那时我们没有资金,无法扩大规模。或许因为那时的 npm 团队不擅长向投资方讲动听的故事,或许那些潜在的投资者也知道我们拿不出金光闪闪的赚钱方案。我不太确定到底是哪个原因。但总之,团队的资金只够维持免费的 JavaScript 服务,但无力做出像样的产品来卖钱。终于,创业资金即将给到期。终于,到了一个不成功便成仁的关口。 -Follow the money. +一切向钱看。 -So we’re in this situation we’re in now, where npm Inc hasn’t fully had its reckoning with its true destiny as a way to make money for some people who aren’t Javascript programmers. Maybe it's going to show growth. Maybe it's going to die. Maybe it's going to do something none of us like to make money, leveraging that enormous data trove that is their view of the development habits of every single one of you. +因此,我们的处境变成了现在这样,npm 公司还没有完全认清自己作为某些人的赚钱工具的命运,而这些人并不是 JavaScript 开发者。也许它会成功,也许它会破产。也许它会用我们都讨厌的方式赚钱,从你们每个用户的大量宝贵数据中榨取利润。 -We are all aware now that npm doesn't love us, and it doesn't love its employees either. It has no more good will from us, but we are all still installing packages through it. I, for one, find this situation uneasy. I suspect it won't last very long. +现在,我们都清楚 npm 并不爱我们,也不爱它的员工。我们对其怒目而视,却同时仍在用它安装代码包。我,个人来说,对这种现状不能安之若素。我怀疑这种现状不会长久。 -Our story didn't have to go this way. Our major actors could have chosen differently, right up to and including their choices in recent months. But here we are. Our commons are in the hands of something I do not believe we can trust, because its incentives are not aligned with what we, as a community of Javascript programmers, need from it. +其实剧情不必是这种走向。在近几个月及之前的过程中,各位主演本可以选择不同的道路。但现在却变成了这样。掌握着我们的公共资源的角色,是不值得我们信任的角色,因为其初衷并不与我们这些 JavaScript 社区成员的诉求相合。 -What are we going to do about it? +那我们如何应对这种局面呢? -One answer is, we do nothing. Say we can't do anything. We made our community decision in 2013 and we're stuck with it. We wait for npm Inc to fail and when it does, we have a nasty couple of months replacing the registry. +有一种答案是按兵不动,因为我们反正也无计可施。我们的社区在 2013 年时就已经做出了抉择,并且一条道走到黑。我们只能寄希望于 npm 公司的倒闭,等它真倒闭了,我们再用难熬的几个月时间来找到替代品。 -I don’t much like this answer. +我不太喜欢这个答案。 -Imagine npm Inc in the hands of a corporate pirate, somebody who takes over ailing companies adversarially and shakes them down for change. Somebody like this might have no incentive to keep the public registry running, or might consider it something that can be mined for your usage data. We, npm’s users, are held hostage to the decisions of a board of directors that hasn't done a very good job thus far, so I find this scenario quite possible. +设想一下 npm 被某个海盗公司(专门收购不景气的公司并强制改革)掌管的情形。这种接管者可能没兴趣让公共注册中心维持运转,可能还会想要滥用用户数据。我们,npm 的用户,受制于董事会的决策,而鉴于董事会迄今为止表现不佳,上述设想变为现实的可能性很大。 -This is a more optimistic outcome. A larger corporate savior might swoop in and rescue npm, our beloved package registry, and keep our commons alive. But even if the registry ends up owned by a secure company, I think this merely punts the problem down the road. Microsoft is a benevolent actor today, but they sure haven't always been. Google was once a benevolent actor, but they're now acting exactly like the monopolist Microsoft was in the 90s. +还有一种更乐观的结局。一个体量更大的救世主公司从天而降,拯救我们心爱的 npm 于水火之中,让我们的公共资源焕发生机。但即使掌管 npm 的公司很靠谱,我认为这还是治标不治本。Microsoft 如今扮演着仁慈的角色,但他们肯定不会一直如此。Google 也曾扮演过仁慈的角色,但如今活活一副上个世纪九十年代 Microsoft 那种垄断者的嘴脸。 -I'd like us to avoid a repeat of this talk a decade from now. +我希望我们能够避免重蹈过去十年的覆辙。 -I think, in the end, I agree with Ryan Dahl. Javascript's package registry should not be privately controlled. I think centralization is a burden that will inevitably lead toward private control, because, well, the servers always cost money. If we can diffuse the load out among many people, we can share the burden. +我觉得我还是赞同 Ryan Dahl 的观点。npm 不应该私有化。我认为中心化是个负担,会不可避免地导致私有控制,因为服务器总是一笔开销。如果我们大家共同分担,我们就能扛住这个负担。 -This probably sounds impossible to you all. You're thinking that npm is entrenched. A few months ago I might have agreed, but then the company set on fire every bit of goodwill it had ever had for no reason anybody can see, all the trust people like me had ever earned it as benevolent stewards. Just poof. Up in flames. They’re so bad at community, and values, and people, it looks intentional. [exasperated gesture] +这可能让你们都觉得不可能。你们都觉得 npm 根深蒂固。几个月前我也这么想,但后来 npm 公司出于所有人都不知道的原因,一把火将其良心烧得干干净净,包括我在内的所有信任者都曾愿意为其良心鞠躬尽瘁。就那么“噗”的一声,付之一炬。他们对 JavaScript 社区、对价值观、对我们开发者如此恶劣,他们是蓄意为之!(此处配合愤怒的手势) -Also, I’m not much a person for hand-wringing. The do-nothing answer doesn’t sit well with me as a human. +而且,坐以待毙不是我的风格。无计可施在我的字典里根本不存在。 -The other confession I’ll make is that I believe in the potlatch economy despite everything. I think it's good for us as humans to give things to each other, and I think I'm at peace with the idea that some corporations will make money from it. +另外我要说的是,尽管发生了这一切,但我仍然信仰 Potlatch 文化。我认为互相分享对我们有益,而即使有人利用这一点赚钱,我也能心平气和地面对。 -So I have an announcement. I would like to give away something to you all today. And it’s not just me giving this away. My colleague Chris Dickinson and I have been working on this together. +因此我要在这里宣布。我今天要和你们大家分享一些东西。而且分享者不仅仅是我一个人。我和同事 Chris Dickinson 一直在合力做这件事。 -Today I'd like to introduce you to entropic, a federated package manager for Javascript. +今天我要向你们介绍 Entropic,一个 JavaScript 联合包管理工具。 -Entropic is Apache 2 licensed, and everything you need to run your own registry is included. +Entropic 基于 Apache 2 协议,仅需引入就可以运行你自己的注册中心。 -Entropic comes with its own cli, which we’re calling `ds` , the entropy delta,. +Entropic 带有自己的脚手架工具,叫做 `ds`,即 entropy delta。 -Entropic offers a new API for publication and for installation, one that drastically reduces the amount of network traffic needed. The unit of installation is a single file, not a tarball. +Entropic 提供全新的发布和安装 API,极大地降低了网络请求量。安装单元就是一个文件,而不是一个大包东西。 -Entropic is federated. You can depend on packages from any other Entropic instance, and your home instance will mirror all your dependencies for you so you remain self-sufficient. +Entropic 是联合性质的。你可以使用来自其他 Entropic 实例的依赖包,并且你的本地实例会把你所有的依赖包进行镜像备份,这样就可以自给自足了。 -Entropic will mirror all packages you install from the legacy package manager. +Entropic 还会备份你从遗留的包管理工具安装的所有依赖包。 -The requirements list is short, and we’ve dockerized it all. +安装要求非常简单,我们已经将其容器化了。 -You sign in with your GitHub account, and any oauth provider is pluggable. +你可以用 GitHub 账号登录,且支持其他任何 OAuth 认证提供方。 -[ show a short video of a demo ] +(在此处播放一小段 demo 视频) -The project is only a month old. It's not ready for anybody but people on the bleeding edge to use yet, but it's self-hosting already. +这个项目才刚刚满月。我们还没准备好将它推到大众视线内,但对于喜欢尝鲜的开发者来说,它足以让你把玩。 -There are more features, and a fairly huge to-do list. It doesn’t have a web site to speak of right now, for instance, and it needs one. The repo has a long list of issues if you’re interested. +Entropic 还有很多功能,并且我们列了长长的待开发功能清单。比如,Entropic 目前还缺个官方网站。如果感兴趣,你可以去 GitHub 仓库看看长长的一列 issue。 -What are my goals with entropic? +Entropic 的目标是什么? -First, I want to prove to you all that there are options beyond doing nothing. We don’t have to sit here and wait for npm to shut down before we do something. Be optimistic. Be pro-active. You have power. +首先,我想向大家证明,除了无计可施,我们还有其他选择。我们不必坐在原地等着 npm 倒闭再采取行动。乐观一些,积极一些,咱们有这个能力。 -Second, Chris and I are among a handful of humans who deeply understand the problems a language package registry needs to solve, particularly at scale. That expertise is needed by our community right now, and we want to share it. We choose to give something valuable away. Potlatch: this is our gift to you of our expertise. +其二,Chris 和我以及其他几个人深切理解一个开源包注册中心需要解决的问题,特别是规模问题。我们的社区此刻迫切需要这种专门的经验,而我们几个也迫切需要分享这类经验。我们选择把有价值的东西分享出来。遵从 Potlatch 文化:把我们的经验做为礼物赠与诸君。 -Third, I think it’s right that the pendulum is swinging away from centralization and I want to lend my push to the swing. The last decade has been about consolidation and monolithic services, but the coming decade is going to be federated. Federation spreads out costs. It spreads out control. It spreads out policy-making. It hands control of your slice of our language ecosystem to you. +第三,我认为中心化终会式微,我愿意在这个过程中出一把力。过去的十年,是整合统一服务的十年,但接下来的十年,将是属于联合化的十年。联合化将分担开销。联合化将分化集权。联合化将分解一言堂。联合化将我们的语言生态掌控权交到我们自己手中。 -My hope is that by giving Entropic away, I’ll help us take our language commons back. We'll take it back so we’re not held hostage by the needs of venture capitalists. Take it back so it's possible for us all to make a decision different from the decision we all made at the end of 2013. Take it back. As a community of millions of developers, it's in our power to take it back. Entropic is our kick-start to this movement to take our language back. +我的愿景,是通过 Entropic,帮助我们大家把公共资源夺回来。夺回来,我们才不再是风投资本家案板上的鱼肉。夺回来,我们才能走一条与 2013 年年末决定走上的不一样的路。夺回来!作为数以百万计的开发者的共同体,我们有这个能力夺回来。此刻,Entropic 就是我们夺回 JavaScript 公共资源的冲锋号。 -There’s a lot more to say about Entropic, and a lot more work to do on it, but it’s time for me to share it with you. +关于 Entropic,还有很多想说,还有很多想做,但是时候把它分享给大家了。 -If we’ve learned that npm doesn’t love you—and it doesn’t-- and we’ve learned that a private company shouldn’t control the commons—and it shouldn‘t, then it’s time to move with your feet. I believe the Node community is good, comprised of good people with the talent and the interest in building amazing things. We should not be defined by a single corporation, and we shouldn’t let them control our destiny when so much—the future of the web!—is at stake. We need to take back control, and to do that, we need to finish building Entropic, together. +如果我们明白了 npm 不爱我们 —— 确实不爱,如果我们明白了一个私有公司不该掌控公共资源 —— 确实不该,那么,是时候迈开脚步了。我坚信这个由优良开发者组成的优良社区,他们拥有惊艳世界的才华和意志。在这个紧要关头,Web 的未来已经危如累卵,我们的规则不应该让某个公司来定义,我们的命运不应该让某个公司来掌控!我们把控制权夺回来,而为了夺回来,我们需要共同完善 Entropic。 -With love from two human beings, from us to you. Please do something good with this. +我们要把对这份对社区的热爱,传递给大家,由己及人。请大家务必善待这份爱。 > 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 From 37cdaa3cb54806af7d2d42604ba31ddf7fe27ef8 Mon Sep 17 00:00:00 2001 From: Mirosalva <5740409+Mirosalva@users.noreply.github.com> Date: Tue, 16 Jul 2019 17:49:50 +0800 Subject: [PATCH 47/68] =?UTF-8?q?Git=EF=BC=9A=E9=80=8F=E8=BF=87=E5=91=BD?= =?UTF-8?q?=E4=BB=A4=E5=AD=A6=E6=A6=82=E5=BF=B5=20=E2=80=94=E2=80=94=20?= =?UTF-8?q?=E7=AC=AC=E4=BA=8C=E9=83=A8=E5=88=86=20(#6113)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 翻译『learn-git-concepts-not-commands-2』; * 完成翻译『learn-git-concepts-not-commands-2』; * 根据校对意见修改 『learn-git-concepts-not-commands-2』; * 添加译者; * 修正译文部分格式问题 * review 『learn-git-concepts-not-commands-2』; * review 『learn-git-concepts-not-commands-2』; * 修正译文格式问题 * review 『learn-git-concepts-not-commands-2』; --- TODO1/learn-git-concepts-not-commands-2.md | 348 ++++++++++----------- 1 file changed, 174 insertions(+), 174 deletions(-) diff --git a/TODO1/learn-git-concepts-not-commands-2.md b/TODO1/learn-git-concepts-not-commands-2.md index 3b198f973b5..bee1d85ea02 100644 --- a/TODO1/learn-git-concepts-not-commands-2.md +++ b/TODO1/learn-git-concepts-not-commands-2.md @@ -2,117 +2,117 @@ > * 原文作者:[Nico Riedmann](https://dev.to/unseenwizzard) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/learn-git-concepts-not-commands-2.md](https://github.com/xitu/gold-miner/blob/master/TODO1/learn-git-concepts-not-commands-2.md) -> * 译者: -> * 校对者: +> * 译者:[Mirosalva](https://github.com/Mirosalva) +> * 校对者:[shixi-li](https://github.com/shixi-li),[Moonliujk](https://github.com/Moonliujk) -# Learn git concepts, not commands- Part 2 +# Git:透过命令学概念 —— 第二部分 -**An interactive git tutorial meant to teach you how git works, not just which commands to execute.** +**用交互式的教程教你 Git 的原理,而非罗列常用命令。** -So, you want to use git right? +所以,你想正确地使用 Git 吗? -But you don't just want to learn commands, you want to understand what you're using? +但你肯定不想仅仅学一些操作命令,你还想要理解其背后的原理,对吧? -Then this is meant for you! +那么本文就是为你量身定做的! -Let's get started! +让我们快点开动吧! --- -> Based on the general concept from Rachel M. Carmena's blog post on [How to teach Git](https://rachelcarmena.github.io/2018/12/12/how-to-teach-git.html). -> -> While I find many git tutorials on the internet to be too focused on what to do instead of how things work, the most invaluable resource for both (and source for this tutorial!) are the [git Book](https://git-scm.com/book/en/v2) and [Reference page](https://git-scm.com/docs). -> -> So if you're still interested when you're done here, go check those out! I do hope the somewhat different concept of this tutorial will aid you in understanding all the other git features detailed there. +> 本文的落笔点基于 Rachel M. Carmena 撰写的 [**如何教授 Git**](https://rachelcarmena.github.io/2018/12/12/how-to-teach-git.html) 一文中提及的常规概念。 +> +> 网上有很多重方法轻原理的 Git 教程,但我还是挖掘到了兼得二者的宝贵资源(也是本教程的灵感源泉),那就是 [*git Book*](https://git-scm.com/book/en/v2) 和 [*Reference page*](https://git-scm.com/docs)。 +> +> 因此,如果你读完了本文还意犹未尽,就快点击上面两个链接一探究竟吧!我真心希望本教程中介绍的概念,能帮你理解另外两篇文章中详解的其他 Git 功能 建议按照顺序阅读本系列文章: -- [Learn git concepts, not commands - Part 1](https://github.com/xitu/gold-miner/blob/master/TODO1/learn-git-concepts-not-commands-1.md) -- [Learn git concepts, not commands - Part 2](https://github.com/xitu/gold-miner/blob/master/TODO1/learn-git-concepts-not-commands-2.md) -- [Learn git concepts, not commands - Part 3](https://github.com/xitu/gold-miner/blob/master/TODO1/learn-git-concepts-not-commands-3.md) +- [Git:透过命令学概念 —— 第一部分](https://github.com/xitu/gold-miner/blob/master/TODO1/learn-git-concepts-not-commands-1.md) +- [Git:透过命令学概念 —— 第二部分](https://github.com/xitu/gold-miner/blob/master/TODO1/learn-git-concepts-not-commands-2.md) +- [Git:透过命令学概念 —— 第三部分](https://github.com/xitu/gold-miner/blob/master/TODO1/learn-git-concepts-not-commands-3.md) --- -- [Learn git concepts, not commands- Part 2](#learn-git-concepts-not-commands--part-2) - - [Merging](#merging) - - [Fast-Forward merging](#fast-forward-merging) - - [Merging divergent branches](#merging-divergent-branches) - - [Resolving conflicts](#resolving-conflicts) - - [Rebasing](#rebasing) - - [Resolving conflicts](#resolving-conflicts-1) - - [Updating the **Dev Environment** with remote changes](#updating-the-dev-environment-with-remote-changes) - - [Fetching Changes](#fetching-changes) - - [Pulling Changes](#pulling-changes) - - [Stashing changes](#stashing-changes) - - [Pulling with Conflicts](#pulling-with-conflicts) +- [Git:透过命令学概念 —— 第二部分](#Git%E9%80%8F%E8%BF%87%E5%91%BD%E4%BB%A4%E5%AD%A6%E6%A6%82%E5%BF%B5--%E7%AC%AC%E4%BA%8C%E9%83%A8%E5%88%86) + - [合并](#%E5%90%88%E5%B9%B6) + - [快进合并](#%E5%BF%AB%E8%BF%9B%E5%90%88%E5%B9%B6) + - [合并相异的分支](#%E5%90%88%E5%B9%B6%E7%9B%B8%E5%BC%82%E7%9A%84%E5%88%86%E6%94%AF) + - [解决冲突](#%E8%A7%A3%E5%86%B3%E5%86%B2%E7%AA%81) + - [变基](#%E5%8F%98%E5%9F%BA) + - [解决冲突](#%E8%A7%A3%E5%86%B3%E5%86%B2%E7%AA%81-1) + - [更新远程变更到**本地工作环境**](#%E6%9B%B4%E6%96%B0%E8%BF%9C%E7%A8%8B%E5%8F%98%E6%9B%B4%E5%88%B0%E6%9C%AC%E5%9C%B0%E5%B7%A5%E4%BD%9C%E7%8E%AF%E5%A2%83) + - [获取更新](#%E8%8E%B7%E5%8F%96%E6%9B%B4%E6%96%B0) + - [拉取更新](#%E6%8B%89%E5%8F%96%E6%9B%B4%E6%96%B0) + - [储藏变更](#%E5%82%A8%E8%97%8F%E5%8F%98%E6%9B%B4) + - [包含冲突的拉取](#%E5%8C%85%E5%90%AB%E5%86%B2%E7%AA%81%E7%9A%84%E6%8B%89%E5%8F%96) --- -## Merging +## 合并 -As you and everyone else will generally be working on branches, we need to talk about how to get changes from one branch into the other by **merging** them. +我们所有人一般都会工作在分支上,我们需要讨论下如何通过**合并**来从一个分支上获取变更到另一个分支上。 -We've just changed `Alice.txt` on the `change_alice` branch, and I'd say we're happy with the changes we made. +我们刚在 `change_alice` 分支上修改了 `Alice.txt`,我想说我们对所做的改变感到满意。 -If you go and `git checkout master`, the `commit` we made on the other branch will not be there. To get the changes into master we need to `merge` the `change_alice` branch **into** master. +如果你接着执行 `git checkout master` 命令,那么我们在其他分支上创建的 `提交` 无法在此看到为了将变更弄到 master 分支,我们需要 `合并` `change_alice` 分支**到** master 分支上。 -Note that you always `merge` some branch **into** the one you're currently at. +注意:你总是将某个分支 `合并` 到当前分支。 -### Fast-Forward merging +### 快进合并 -As we've already `checked out` master, we can now `git merge change_alice`. +既然我们已经执行了 `checked out` 来切换到 master 分支,现在我们可以执行 `git merge change_alice` 合并命令。 -As there are no other **conflicting** changes to `Alice.txt`, and we've changed nothing on **master**, this will go through without a hitch in what is called a **fast forward** merge. +由于 `Alice.txt` 并没有其他**冲突**变更,我们在 **master** 分支未做修改,因此合并将在所谓的**快进**合并中进行。 -In the diagrams below, you can see that this just means that the **master** pointer can simply be advanced to where the **change_alice** one already is. +在下面的图表中,我们可以看到,这仅仅意味着:**master** 的指针会被简单地前进到 **change_alice** 分支存在的位置。 -The first diagram shows the state before our `merge`, **master** is still at the commit it was, and on the other branch we've made one more commit. +第一张图显示了我们执行 `合并` 前的状态,**master** 指针仍处于它之前的提交位置,同时另一个分支上我们又做了一次提交。 -[![Before fast forward merge](https://res.cloudinary.com/practicaldev/image/fetch/s--sS6CJ1Rg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/before_ff_merge.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--sS6CJ1Rg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/before_ff_merge.png) +[![快进合并之前](https://res.cloudinary.com/practicaldev/image/fetch/s--sS6CJ1Rg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/before_ff_merge.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--sS6CJ1Rg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/before_ff_merge.png) -The second diagram shows what has changed with our `merge`. +第二张图显示了在我们 `合并` 之后发生了什么变化。 -[![After fast forward merge](https://res.cloudinary.com/practicaldev/image/fetch/s--K_hHy8zA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/ff_merge.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--K_hHy8zA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/ff_merge.png) +[![快进合并之后](https://res.cloudinary.com/practicaldev/image/fetch/s--K_hHy8zA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/ff_merge.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--K_hHy8zA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/ff_merge.png) -### Merging divergent branches +### 合并相异的分支 -Let's try something more complex. +让我们试一些更复杂的。 -Add some text on a new line to `Bob.txt` on **master** and commit it. +在 master 分支的 `Bob.txt` 文件新行中添加一些文字,然后提交它。 -Then `git checkout change_alice`, change `Alice.txt` and commit. +接着执行 `git checkout change_alice` 命令,改变 `Alice.txt` 文件并提交。 -In the diagram below you see how our commit history now looks. Both **master** and `change_alice` originated from the same commit, but since then they **diverged**, each having their own additional commit. +在下图中,你可以看到我们的提交历史现在的样子。**master** 和 `change_alice` 分支都源于同一个提交,但那之后它们发生了**分歧** ,每个分支都有自己额外的提交。 -[![Divergent commits](https://res.cloudinary.com/practicaldev/image/fetch/s--NKM59jTn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/branches_diverge.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--NKM59jTn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/branches_diverge.png) +[![不同的提交](https://res.cloudinary.com/practicaldev/image/fetch/s--NKM59jTn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/branches_diverge.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--NKM59jTn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/branches_diverge.png) -If you now `git merge change_alice` a fast-forward merge is not possible. Instead your favorite text editor will open and allow you to change the message of the `merge commit` git is about to make in order to get the two branches back together. You can just go with the default message right now. The diagram below shows the state of our git history after we the `merge`. +如果你现在使用命令 `git merge change_alice` 来执行一个快进合并是不可能的了。取代它的是,你最爱的文本编辑器将会打开,并且允许你修改 `合并提交` 操作的提交信息,git 即将执行这个提交从而将两个分支重新保持一致。你现在使用默认提交信息就行。下图显示了我们在执行 `合并` 后的 git 历史状态。 -[![Merging branches](https://res.cloudinary.com/practicaldev/image/fetch/s--btBTCeUD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/merge.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--btBTCeUD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/merge.png) +[![合并分支](https://res.cloudinary.com/practicaldev/image/fetch/s--btBTCeUD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/merge.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--btBTCeUD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/merge.png) -The new commit introduces the changes that we've made on the `change_alice` branch into master. +新的提交将我们在 `change_alice` 分支上的修改引入到 master 分支。 -As you'll remember from before, revisions in git, aren't only a snapshot of your files but also contain information on where they came from from. Each `commit` has one or more parent commits. Our new `merge` commit, has both the last commit from **master** and the commit we made on the other branch as it's parents. +正如你之前记得那样,git 中的修订不仅仅是文件的快照,还包含了它们来自何处的一些信息。每次 `提交` 都包含一个或多个父级提交信息。我们的新 `合并` 提交包含了 **master** 分支的最后提交,以及我们在另一个分支上的提交来作为这次合并的父级提交。 -### Resolving conflicts +### 解决冲突 -So far our changes haven't interfered with each other. +目前为止,我们的修改都没有相互干扰。 -Let's introduce a **conflict** and then **resolve** it. +让我们介绍一种**冲突**,然后**解决**它。 -Create and `checkout` a new branch. You know how, but maybe try using `git checkout -b` to make your live easier. +创建一个新分支,然后将它 `检出`。你知道如何操作,不过或许可以使用 `git checkout -b` 命令为你减少麻烦。 -I've called mine `bobby_branch`. +我把它命名为 `bobby_branch`。 -On the branch we'll make a change to `Bob.txt`. +在这个分支上,我们会修改 `Bob.txt` 文件。 -The first line should still be `Hi!! I'm Bob. I'm new here.`. Change that to `Hi!! I'm Bobby. I'm new here.` +第一行应该仍然是 `Hi!! I'm Bob. I'm new here.`,把它修改成 `Hi!! I'm Bobby. I'm new here.`。 -Stage and then `commit` your change, before you `checkout` **master** again. Here we'll change that same line to `Hi!! I'm Bob. I've been here for a while now.` and `commit` your change. +暂存文件之后,在你再次 `检出` master 分支之前,`提交` 你的修改。在 master 分支我们将同一行修改为 `Hi!! I'm Bob. I've been here for a while now.`,接着 `提交` 该修改。 -Now it's time to `merge` the new branch into **master**. +现在是将新分支 `合并` 到 **master** 的时候了。 -When you try that, you'll see the following output +如果你尝试这么做,你将会看到如下的结果: ``` Auto-merging Bob.txt @@ -120,15 +120,15 @@ CONFLICT (content): Merge conflict in Bob.txt Automatic merge failed; fix conflicts and then commit the result. ``` -The same line has changed on both of the branches, and git can't handle this on it's own. +两个分支都修改了同一行,此时 git 工具无法自己完全处理这种情况。 -If you run `git status` you'll get all the usual helpful instructions on how to continue. +如果你运行 `git status` 命令,你会获取到用来指导接下来如何继续的所有常见帮助命令。 -First we have to resolve the conflict by hand. +首先我们必须手动解决冲突。 -> For an easy conflict like this one your favorite text editor will do fine. For merging large files with lots of changes a more powerful tool will make your life much easier, and I'd assume your favorite IDE comes with version control tools and a nice view for merging. +> 对于像这个简单冲突来说,你最喜爱的文本编辑器就够用了。而对于合并含有很多变化的多个文件来说,使用更强大的工具会让你轻松不少,我建议选用包含版本控制工具,具有友好合并界面的你最喜爱的 IDE。 -If you open `Bob.txt` you'll see something similar to this (I've truncated whatever we might have put on the second line before): +如果你打开 `Bob.txt` 文件,你会看到一些类似下面的内容(我已经截断了之前可能放在第二行的其他内容): ``` <<<<<<< HEAD @@ -136,144 +136,144 @@ Hi! I'm Bob. I've been here for a while now. ======= Hi! I'm Bobby. I'm new here. >>>>>>> bobby_branch -[... whatever you've put on line 2] +[...你在第 2 行传入的随便什么内容] ``` -On top you see what has changed in `Bob.txt` on the current HEAD, below you see what has changed in the branch we're merging in. +在上面你可以看到当前 HEAD 上 `Bob.txt` 发生的变化,在下面你可以看到我们正尝试合并进来的分支所做的更改。 -To resolve the conflict by hand, you'll just need to make sure that you end up with some reasonable content and without the special lines git has introduced to the file. +为了手工解决冲突,你只需要确保文件最终保留一些合理内容,而不包含 git 引入文件的特殊行。 -So go ahead and change the file to something like this: +所以继续修改文件为下面这种内容: ``` Hi! I'm Bobby. I've been here for a while now. [...] ``` -From here what we're doing is exactly what we'd do for any changes. +从这里开始,我们即将要做的事是适用于任何变更。 -We **stage** them when we `add Bob.txt`, and then we `commit`. +在我们执行 `add Bob.txt` 添加文件后,**暂存**这些变更,然后执行 `提交`。 -We already know the commit for the changes we've made to resolve the conflict. It's the **merge commit** that is always present when merging. +我们已经了解为解决冲突所做的变更提交,就是合并过程中一直都有的**合并提交**。 -Should you ever realize in the middle of resolving conflicts that you actually don't want to follow through with the `merge`, you can just `abort` it by running `git merge --abort`. +实际上你应该意识到在解决冲突的过程中,如果你不想继续 `合并` 进程,你可以通过运行 `git merge --abort` 命令直接 `中止` 它。 -## Rebasing +## 变基 -Git has another clean way to integrate changes between two branches, which is called `rebase`. +Git 有另外一种纯净方式来集成两个分支的变化,叫做 `变基`。 -We still recall that a branch is always based on another. When you create it, you **branch away** from somewhere. +我们始终记得一个分支总是基于另外一个分支。当你创建它时,从某处开始**分叉**。 -In our simple merging example we branched from **master** at a specific commit, then committed some changes on both **master** and the `change_alice` branch. +在我们简单的合并样例中,我们从 **master** 分支的某次提交创建了一个分支,然后提交了在 **master** 和 `change_alice` 分别提交了一些变更。 -When a branch is diverging from the one it's based on and you want to integrate the latest changes back into your current branch, `rebase` offers a cleaner way of doing that than a `merge` would. +当一个分支相对于它所基于的分支产生了改变,如果你想要把最新的变更整合到你当前的分支,`变基` 提供了一种比 `合并` 更加纯净的处理方式。 -As we've seen, a `merge` introduces a **merge commit** in which the two histories get integrated again. +正如你所看到的,一次 `合并` 引入了一个**合并提交**,这个过程中两边的历史记录得到整合。 -Viewed simply, rebasing just changes the point in history (the commit) your branch is based on. +很容易看得出,变基仅仅改变了你的分支所依赖的历史记录点(创建分支所基于的某次提交)。 -To try that out, let's first checkout the **master** branch again, then create/checkout a new branch based on it. +为了尝试这一点,我们首先将 **master** 分支再次检出,然后基于它来创建/检出一个新分支。 -I called mine `add_patrick` and I added a new `Patrick.txt` file and committed that with the message 'Add Patrick'. +我称自己的新分支为 `add_patrick`,然后我添加了一个新文件 `Patrick.txt`,然后以信息 “Add Patrick” 提交了该文件。 -When you've added a commit to the branch, get back to **master**, make a change and commit it. I added some more text to `Alice.txt`. +在你为该分支添加了一条提交后,返回 **master** 分支,做一点修改然后提交它。我做的修改是为 `Alice.txt` 文件多加了一些文本。 -Like in our merging example the history of these two branches diverges at a common ancestor as you can see in the diagram below. +就像我们合并样例中那样,两个分支有公共祖先,然而历史是不同的,你可以从下图中看出: -[![History before a rebase](https://res.cloudinary.com/practicaldev/image/fetch/s--nTsD2ONw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/before_rebase.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--nTsD2ONw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/before_rebase.png) +[![一次变基之前的历史记录](https://res.cloudinary.com/practicaldev/image/fetch/s--nTsD2ONw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/before_rebase.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--nTsD2ONw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/before_rebase.png) -Now let's `checkout add_patrick` again, and get that change that was made on **master** into the branch we work on! +现在让我们再执行 `checkout add_patrick` 命令,然后把 **master** 上做的修改获取到我们正在操作的分支上! -When we `git rebase master`, we re-base our `add_patrick` branch on the current state of the **master** branch. +当我们执行 `git rebase master` 命令,我们让 `add_patrick` 分支重新以当前状态的 **master** 分支做了基准。 -The output of that command gives us a nice hint at what is happening in it: +上面这条命令为我们提供了目前操作的友好提示: ``` First, rewinding head to replay your work on top of it... Applying: Add Patrick ``` -As we remember **HEAD** is the pointer to the current commit we're at in our **Dev Environment**. +我们知道 **HEAD** 是我们所在的**工作环境**中当前提交的指针。 -It's pointing to the same place as `add_patrick` before the rebase starts. For the rebase, it then first moves back to the common ancestor, before moving to the current head of the branch we want to re-base ours on. +在变基操作执行之前,它的指向与 `add_patrick` 分支一致。发生了变基,它会首先移回到两个分支的公共祖先,然后移动到我们想要定为基点的那个分支的当前顶点。 -So **HEAD** moves from the **0cfc1d2** commit, to the **7639f4b** commit that is at the head of **master**. +所以 **HEAD** 移动到 **0cfc1d2** 这次提交,然后到 **7639f4b** 这次提交,它是位于 **master** 分支的顶点。 -Then rebase applies every single commit we made on our `add_patrick` branch to that. +然后变基操作会将我们在 `add_patrick` 分支上做的每一个提交都应用到那个顶点上。 -To be more exact what **git** does after moving **HEAD** back to the common ancestor of the branches, is to store parts of every single commit you've made on the branch (the `diff` of changes, and the commit text, author, etc.). +为了更精确了解 **git** 把 **HEAD** 指针移回到分支的公共祖先过程中做了什么,可以把你在被操作的分支上每次提交都存储一部分(修改的 `差异点`、提交信息、作者等等。)。 -After that it does a `checkout` of the latest commit of the branch you're rebasing on, and then applies each of the stored changed **as a new commit** on top of that. +在上面操作之后,你正在变基的分支需要 `检出` 最新的提交,然后把存下的所有变化**以一条新提交**应用到前面的提交之上。 -So in our original simplified view, we'd assume that after the `rebase` the **0cfc1d2** commit doesn't point to the common ancestor anymore in it's history, but points to the head of master. +所以在我们原先简单的视图中,我们认为在 `变基` 之后,**0cfc1d2** 这次提交不再指向它历史中原公共祖先,而是指向 master 分支的顶部。 -In fact the **0cfc1d2** commit is gone, and the `add_patrick` branch starts with a new **0ccaba8** commit, that has the latest commit of **master** as its ancestor. +事实上,**0cfc1d2** 这次提交消失了,并且 `add_patrick` 分支以一个新提交 **0ccaba8** 为开始,它以 **master** 分支的最新提交作为公共祖先。 -We made it look, like our `add_patrick` was based on the current **master** not an older version of it, but in doing so we re-wrote the history of the branch. +我们让它看起来就像,`add_patrick` 分支是以当前 **master** 分支为基点,而不是分支的较旧版本,不过我们这样做相当于重写了该分支的历史。 -At the end of this tutorial we'll learn a bit more about re-writing history and when it's appropriate and inappropriate to do so. +在本教程的末尾,我们会多学习一些重写历史以及什么时候适宜和不适宜这么做。 -[![History after rebase](https://res.cloudinary.com/practicaldev/image/fetch/s--rV897ytW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/rebase.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--rV897ytW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/rebase.png) +[![变基之后的历史记录](https://res.cloudinary.com/practicaldev/image/fetch/s--rV897ytW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/rebase.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--rV897ytW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/rebase.png) -`Rebase` is an incredibly powerful tool when you're working on your own development branch which is based on a shared branch, e.g. the **master**. +当你自己的工作分支是基于一个共享分支时,例如 **master** 分支,`变基` 是一种相当强大的工具。 -Using rebase you can make sure that you frequently integrate the changes other people make and push to **master**, while keeping a clean linear history that allows you to do a `fast-forward merge` when it's time to get your work into the shared branch. +使用变基操作,可以确保你能经常整合别人提交到 **master** 分支的变更和推送,并且保证一条干净线性的历史,在你的工作文本需要引入到共享分支时,这种历史可以做 `快进合并`。 -Keeping a linear history also makes reading or looking at (try out `git log --graph` or take a look at the branch view of **GitHub** or **GitLab**) commit logs much more useful than having a history littered with **merge commits**, usually just using the default text. +相对于包含**合并提交**的凌乱历史,保持历史的线性也可以使提交日志更加有用(试一下 `git log --graph`,或者看一下 **GitHub** 或 **GitLab** 的分支视图)。 -### Resolving conflicts +### 解决冲突 -Just like for a `merge` you may run into conflicts, if you run into two commits changing the same parts of a file. +就像 `合并` 过程中,如果遇到两次提交修改了一个文件中同样位置的内容块,你可能会遇到冲突。 -However when you encounter a conflict during a `rebase` you don't fix it in an extra **merge commit**, but can simply resolve it in the commit that is currently being applied. +然而当你在 `变基` 过程中遇到冲突,你无需在额外的**合并提交**中解决它,却可以在当前正在执行的提交中解决它。 -Again, basing your changes directly on the current state of the original branch. +同样地,将你的修改直接以原始分支的当前状态为基准。 -Actually resolving conflicts while you `rebase` is very similar to how you would for a `merge` so refer back to that section if you're not sure anymore how to do it. +事实上,你在 `变基` 过程中的解决冲突操作非常类似你在 `合并` 中的操作,所以如果你不太确定如何操作的话,可以回过头查看那个小节。 -The only distinction is, that as you're not introducing a **merge commit** there is no need to `commit` your resolution. Simply `add` the changes to the **Staging Environment** and then `git rebase --continue`. The conflict will be resolved in the commit that was just being applied. +唯一的区别在于,由于你没有引入**合并提交**,所以不需要你提交冲突解决结果。只需**添加**变更到**暂存环境**,然后执行 `git rebase --continue` 命令。冲突将会在刚刚执行的提交中得到解决。 -As when merging, you can always stop and drop everything you've done so far when you `git rebase --abort`. +当合并时,你一直都可以停止和丢弃目前你做的所有内容,通过执行 `git rebase --abort` 命令。 -## Updating the **Dev Environment** with remote changes +## 更新远程变更到**本地工作环境** -So far we've only learned how to make and share changes. +目前为止,我们已经学习了如何生成和共享内容变更。 -That fits what you'll do if you're just working on your own, but usually there'll be a lot of people that do just the same, and we're gonna want to get their changes from the **Remote Repository** into our **Dev Environment** somehow. +如果你是独自工作,这些已经够用了。但是通常我们是多人共同处理一项工作,并且我们想要从**远程仓库**以某种方式获取他们的变更到我们自己的**工作环境**中。 -Because it has been a while, lets have another look at the components of git: +由于已经过了一段时间,让我们看一下 git 的组件: -[![git components](https://res.cloudinary.com/practicaldev/image/fetch/s--jSuilYlA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/components.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--jSuilYlA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/components.png) +[![git 组件](https://res.cloudinary.com/practicaldev/image/fetch/s--jSuilYlA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/components.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--jSuilYlA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/components.png) -Just like your **Dev Environment** everyone else working on the same source code has theirs. +就像你的**工作环境**,每个工作在同一份源代码的人都有他们自己的工作环境。 -[![many dev environments](https://res.cloudinary.com/practicaldev/image/fetch/s--l88bjwDT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/many_dev_environments.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--l88bjwDT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/many_dev_environments.png) +[![许多工作环境](https://res.cloudinary.com/practicaldev/image/fetch/s--l88bjwDT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/many_dev_environments.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--l88bjwDT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/many_dev_environments.png) -All of these **Dev Environments** have their own **working** and **staged** changes, that are at some point `committed` to the **Local Repository** and finally `pushed` to the **Remote**. +所有这些**工作环境**都有它们自己的**进行中**和**暂存的**变更,这些会在某个节点被 `提交` 到**本地仓库**,最终 `推送` 到**远程仓库**。 -For our example, we'll use the online tools offered by **GitHub**, to simulate someone else making changes to the **remote** while we work. +我们的例子中,我们会使用 **GitHub** 提供的在线工具,来模拟在我们工作时其他人对**远程仓库**生成的变更。 -Go to your `fork` of this repo on [github.com](https://www.github.com) and open the `Alice.txt` file. +查看你在 [github.com](https://www.github.com) 网上对这个仓库的 fork 分支,打开 `Alice.txt` 文件。 -Find the edit button and make and commit a change via the website. +找到编辑按钮,通过网站来生成和提交一个变更。 -[![github edit](https://res.cloudinary.com/practicaldev/image/fetch/s--ifXKNJi7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/github.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--ifXKNJi7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/github.png) +[![github 编辑](https://res.cloudinary.com/practicaldev/image/fetch/s--ifXKNJi7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/github.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--ifXKNJi7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/github.png) -In this repository I have added a remote change to `Alice.txt` on a branch called `fetching_changes_sample`, but in your version of the repository you can of course just change the file on `master`. +在这个仓库中,我已经在一个叫做 `fetching_changes_sample` 的分支上为 `Alice.txt` 文件添加了一个远程仓库变更,但是在你的该仓库版本,你当然可以直接改变 `master` 分支上这个文件。 -### Fetching Changes +### 获取更新 -We still remember that when you `git push`, you synchronize changes made to the **Local Repository** into the **Remote Repository**. +我们还记得,当你执行 `git push` 命令时,会将**本地仓库**的变更同步到**远程仓库**。 -To get changes made to the **Remote** into your **Local Repository** you use `git fetch`. +为了获取**远程仓库**中的变更到**本地仓库**,你可以使用 `git fetch` 命令。 -This gets any changes on the remote - so commits as well as branches - into your **Local Repository**. +这个操作获取到远程的任何变更到你的**本地仓库**,包含提交和分支。 -Note that at this point, changes aren't integrated into the local branches and thus the **Working Directory** and **Staging Area** yet. +这点要注意,变更还没有被整合到本地分支,更不用说**工作空间**和**暂存区域**。 -[![Fetching changes](https://res.cloudinary.com/practicaldev/image/fetch/s--F6oFwBrc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/fetch.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--F6oFwBrc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/fetch.png) +[![获取更新](https://res.cloudinary.com/practicaldev/image/fetch/s--F6oFwBrc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/fetch.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--F6oFwBrc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/fetch.png) -If you run `git status` now, you'll see another great example of git commands telling you exactly what is going on: +如果你现在执行 `git status` 命令,你会看到 git 命令的另一个很棒的例子,告诉你现在正发生什么: ``` git status @@ -282,20 +282,20 @@ Your branch is behind 'origin/fetching_changes_sample' by 1 commit, and can be f (use "git pull" to update your local branch) ``` -### Pulling Changes +### 拉取更新 -As we have no **working** or **staged** changes, we could just execute `git pull` now to get the changes from the **Repository** all the way into our working area. +由于我们没有**工作中**和**暂存**的变更,我们现在可以执行 `git pull` 命令来从**仓库**中拉取所有变更到我们的工作空间。 -> Pulling will implicitly also `fetch` the **Remote Repository**, but sometimes it is a good idea to do a `fetch` on it's own. -> For example when you want to synchronize any new **remote** branches, or when you want to make sure your **Local Repository** is up to date before you do a `git rebase` on something like `origin/master`. +> 拉取会隐式地 `获取` **远程仓库**,但有时候单独执行 `获取` 是个好选择。 +> 例如,当你想要同步任何新的**远程**分支,或者你想在像 `origin/master` 这种分支上执行 `git rebase` 之前,需要确保你的**本地仓库**是最新的。 -[![Pulling in changes](https://res.cloudinary.com/practicaldev/image/fetch/s--LD07tDxG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/pull.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--LD07tDxG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/pull.png) +[![拉取更新](https://res.cloudinary.com/practicaldev/image/fetch/s--LD07tDxG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/pull.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--LD07tDxG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/UnseenWizzard/git_training/master/img/pull.png) -Before we `pull`, lets change a file locally to see what happens. +在我们 `拉取` 之前,让我们本地修改一个文件来看会发生什么。 -Lets also change `Alice.txt` in our **Working Directory** now! +让我们在**工作空间**再次修改 `Alice.txt` 文件! -If you now try to do a `git pull` you'll see the following error: +如果你现在尝试做一次 `git pull`,你会看到如下错误: ``` git pull @@ -306,46 +306,46 @@ Please commit your changes or stash them before you merge. Aborting ``` -You can not `pull` in any changes, while there are modifications to files in the **Working Directory** that are also changed by the commits you're `pull`ing in. +你无法 `拉取` 任何变更,因为**工作空间**中有些文件被修改,同时你正在拉取进来的提交也有这些文件的变化。 -While one way around this is, to just get your changes to a point where you're confident in them, `add` them to the **Staging Environment**, before you finally `commit` them, this is a good moment to learn about another great tool, the `git stash`. +这种情况的一种解决方法是,为了获取你比较信任的某个点上的变更,在你最终提交它们之前,可以把本地变更 `添加` 到**暂存空间**。但是在你最终提交它们之前,而这是学习另一个很棒工具的一个好时机。 -### Stashing changes +### 储藏变更 -If at any point you have local changes that you do not yet want to put into a commit, or want to store somewhere while you try some different angle to solve a problem, you can `stash` those changes away. +在任何时刻如果你有一些本地变更,还不想放进一个提交中,或者想存在某个地方来以其他某种角度来解决一个问题,你可以将这些变更 `储藏` 起来。 -A `git stash` is basically a stack of changes on which you store any changes to the **Working Directory**. +一次 `git stash` 基本上是一个变更的堆栈,这里面你可以存储对**工作空间**的任何变更。 -The commands you'll mostly use are `git stash` which places any modifications to the **Working Directory** on the stash, and `git stash pop` which takes the latest change that was stashed and applies it the to the **Working Directory** again. +最常用的命令是:`git stash`,它将对**工作空间**的任何修改储藏起来。还有 `git stash pop` 命令,它拿到储藏起来的最近修改,并将其再次应用到**工作空间**。 -Just like the stack commands it's named after `git stash pop` removes the latest stashed change before applying it again. +就如堆栈命令的命名,`git stash pop` 命令在应用变更之前,将最近存储的变更移除。 -If you want to keep the stashed changes, you can use `git stash apply`, which doesn't remove them from the stash before applying them. +如果你想保留储藏的变更,你可以使用 `git stash apply` 命令,这种方式不会在应用变更之前从储藏中移除它们。 -To inspect you current `stash` you can use `git stash list` to list the individual entries, and `git stash show` to show the changes in the latest entry on the `stash`. +为了检查你当前的 `储藏`,你可以使用 `git stash list` 命令来列出各个单独的条目,还可以使用 `git stash show` 命令来显示 `储藏` 中最近条目的变更。 -> Another nice convenience command is `git stash branch {BRANCH NAME}`, which creates a branch, starting from the HEAD at the moment you've stashed the changes, and applies the stashed changes to that branch. +> 另一个好用的命令是 `git stash branch {BRANCH NAME}`,它从当前的 HEAD 开始创建一个分支,此时你储藏了变更,并把它们应用到了新建分支中。 -Now that we know about `git stash`, lets run it to remove our local changes to `Alice.txt` from the **Working Directory**, so that we can go ahead and `git pull` the changes we've made via the website. +现在我们了解了 `git stash` 命令,让我们执行它,用来从**工作空间**中移除我们对 `Alice.txt` 文件做的本地变更,这样我们就可以继续上面的操作,执行 `git pull` 命令来拉取我们在网站上做的远程变更。 -After that, let's `git stash pop` to get the changes back. +在那之后,让我们执行 `git stash pop` 命令来取回本地变更。 -As both the commit we `pull`ed in and the `stash`ed change modified `Alice.txt` you wil have to resolve the conflict, just how you would in a `merge` or `rebase`. -When you're done `add` and `commit` the change. +因为我们 `拉取` 的提交和 `储藏` 的变更都修改了 `Alice.txt` 文件,所以你需要解决冲突,就像在 `合并` 或 `变基` 中你做的那样。 +完成 `添加` 后,提交这个变更。 -### Pulling with Conflicts +### 包含冲突的拉取 -Now that we've understood how to `fetch` and `pull` **Remote Changes** into our **Dev Environment**, it's time to create some conflicts! +现在我们已经理解如何 `获取` 和 `拉取` **远程变更**到我们的**工作环境**,正是制造一些冲突的时候! -Do not `push` the commit that changed `Alice.txt` and head back to your **Remote Repository** on [github.com](https://www.github.com). +不要推送那个修改 `Alice.txt` 文件的提交,回到你位于 [github.com](https://www.github.com) 的**远程仓库** -There we're also again going to change `Alice.txt` and commit the change. +这里我们又要修改 `Alice.txt` 文件并提交它。 -Now there's actually two conflicts between our **Local** and **Remote Repositories**. +现在实际上在我们的**本地**和**远程仓库**之间存在两处冲突。 -Don't forget to run `git fetch` to see the remote change without `pull`ing it in right away. +不要忘了运行 `git fetch` 命令来查看远程的变更,而不是立即 `拉取` 它。 -If you now run `git status` you will see, that both branches have one commit on them that differs from the other. +如果你现在运行 `git status` 命令,你会看到两个分支各有一个与对方不同的提交。 ``` git status @@ -355,35 +355,35 @@ and have 1 and 1 different commits each, respectively. (use "git pull" to merge the remote branch into yours) ``` -In addition we've changed the same file in both of those commits, to introduce a `merge` conflict we'll have to resolve. +另外,我们已经在上面不同提交中修改了同一个文件,为了介绍 `合并` 中的冲突这个概念,所以我们需要解决它。 -When you `git pull` while there is a difference between the **Local** and **Remote Repository** the exact same thing happens as when you `merge` two branches. +当你执行 `git pull` 命令时,而**本地**和**远程仓库**之间存在着差异,就会发生与 `合并` 两个分支过程时同样的事情。 -Additionally, you can think of the relationship between branches on the **Remote** and the one in the **Local Repository** as a special case of creating a branch based on another. +额外的,你可以认为**远程仓库**和**本地仓库**分支之间的关系是一种从一个分支上创建另一个分支的特殊情况。 -A local branch is based on a branches state on the **Remote** from the time you last `fetched` it. +本地分支是基于你从**远程仓库**最近一次获取的分支状态的。 -Thinking that way, the two options you have to get **remote** changes make a lot of sense: +如果以这种方式思考,这两种选项来获取**远程仓库**变化就很有道理: -When you `git pull` the **Local** and **Remote** version of a branch will be `merged`. Just like `merging` branches, this will introduce a _merge commit. +当你执行 `git pull` 命令,**本地**和**远程仓库**的版本就会 `合并`。就像 `合并` 不同分支一样,这会引入一个**合并**提交。 -As any **local** branch is based on it's respective **remote** version, we can also `rebase` it, so that any changes we may have made locally, appear as if they were based on the latest version that is available in the _Remote Repository. +因为任何本地分支都基于它们各自的远程版本,我们也可以对它执行 `变基`,这样做的话我们在本地做的任何变更,都表现为基于远程仓库中的最新可用版本。 -To do that, we can use `git pull --rebase` (or the shorthand `git pull -r`). +为了这么做,我们可以使用 `git pull --rebase` 命令(或者简写`git pull -r`)。 -As detailed in the section on [Rebasing](#rebasing), there is a benefit in keeping a clean linear history, which is why I would strongly recommend that whenever you `git pull` you do a `git pull -r`. +在[变基](#变基)这小节中已经详细介绍了,保持一个干净线性的历史提交记录是有好处的,所以我才强烈建议当你需要执行 `git pull` 命令时,不妨使用 `git pull -r` 替代。 -> You can also tell git to use `rebase` instead of `merge` as it's default strategy when your `git pull`, by setting the `pull.rebase` flag with a command like this `git config --global pull.rebase true`. +> 你也可以告诉 git 使用 `变基` 来代替 `合并`,作为你执行 `git pull` 命令时的默认策略,通过一个像这样 `git config --global pull.rebase true` 的命令来设置 `pull.rebase` 标识。 -If you haven't already run `git pull` when I first mentioned it a few paragraphs ago, let's now run `git pull -r` to get the remote changes while making it look like our new commit just happened after them. +在我介绍前面几个段落之后,如果你还没有执行过 `git pull` 命令的话,让我现在一起执行 `git pull -r` 来获取远程变更吧,让它显得就像我们的新提交位于那些远程变更之后。 -Of course like with a normal `rebase` (or `merge`) you'll have to resolve the conflict we introduced for the `git pull` to be done. +当然就像一个正常的 `变基`(或者 `合并`)操作,你需要解决我们引入的冲突,以便 `git pull` 命令可以完成。 欢迎继续阅读本系列其他文章: -- [Learn git concepts, not commands - Part 1](https://github.com/xitu/gold-miner/blob/master/TODO1/learn-git-concepts-not-commands-1.md) -- [Learn git concepts, not commands - Part 2](https://github.com/xitu/gold-miner/blob/master/TODO1/learn-git-concepts-not-commands-2.md) -- [Learn git concepts, not commands - Part 3](https://github.com/xitu/gold-miner/blob/master/TODO1/learn-git-concepts-not-commands-3.md) +- [Git:透过命令学概念 —— 第一部分](https://github.com/xitu/gold-miner/blob/master/TODO1/learn-git-concepts-not-commands-1.md) +- [Git:透过命令学概念 —— 第二部分](https://github.com/xitu/gold-miner/blob/master/TODO1/learn-git-concepts-not-commands-2.md) +- [Git:透过命令学概念 —— 第三部分](https://github.com/xitu/gold-miner/blob/master/TODO1/learn-git-concepts-not-commands-3.md) > 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 From 0615faf0fbb7886ead401188c9f4d5ede775f8fc Mon Sep 17 00:00:00 2001 From: Lu Liu <2474499153@qq.com> Date: Tue, 16 Jul 2019 19:08:56 +0800 Subject: [PATCH 48/68] =?UTF-8?q?=E5=8C=BA=E5=9F=9F=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E6=9B=B4=E6=94=B9=E5=92=8C=20AndroidViewModel=20=E5=8F=8D?= =?UTF-8?q?=E9=9D=A2=E6=A8=A1=E5=BC=8F=20(#6080)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update locale-changes-and-the-androidviewmodel-antipattern.md * 字母间空格以及译者链接添加 * 对格式进行修改 * Update locale-changes-and-the-androidviewmodel-antipattern.md --- ...es-and-the-androidviewmodel-antipattern.md | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/TODO1/locale-changes-and-the-androidviewmodel-antipattern.md b/TODO1/locale-changes-and-the-androidviewmodel-antipattern.md index 28f2bca5fbb..dffdd9a6614 100644 --- a/TODO1/locale-changes-and-the-androidviewmodel-antipattern.md +++ b/TODO1/locale-changes-and-the-androidviewmodel-antipattern.md @@ -2,23 +2,22 @@ > * 原文作者:[Jose Alcérreca](https://medium.com/@JoseAlcerreca) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/locale-changes-and-the-androidviewmodel-antipattern.md](https://github.com/xitu/gold-miner/blob/master/TODO1/locale-changes-and-the-androidviewmodel-antipattern.md) -> * 译者: -> * 校对者: +> * 译者:[solerji](https://github.com/solerji) -# Locale changes and the AndroidViewModel antipattern +# 区域设置更改和 AndroidViewModel 反面模式 -> TL;DR: Expose resource IDs from ViewModels to avoid showing obsolete data. +> TL;DR:从视图模型中公开资源 ID 以避免显示废弃的数据。 -In a ViewModel, if you’re exposing data coming from resources (strings, drawables, colors…), you have to take into account that ViewModel objects ignore configuration changes such as **locale changes**. When the user changes their locale, activities are recreated but the ViewModel objects are not. +在 ViewModel 中,如果要公开来自资源(字符串、可绘制文件、颜色……)的数据,则必须着重考虑 ViewModel 对象而忽视配置更改,例如**区域设置更改**。当用户更改其区域设置时,活动将重新被创建,但不创建 ViewModel 对象。 -![**The localized string is not updated after a locale change**](https://cdn-images-1.medium.com/max/2000/0*kL5zW7zi_ImPUwHr) +![**本地化字符串在区域设置更改后不更新**](https://cdn-images-1.medium.com/max/2000/0*kL5zW7zi_ImPUwHr) -`AndroidViewModel` is a subclass of `ViewModel` that is aware of the Application context. However, having access to a context can be dangerous if you’re not observing or reacting to the lifecycle of that context. **The recommended practice is to avoid dealing with objects that have a lifecycle in ViewModels.** +`AndroidViewModel` 是已知应用程序上下文的 `ViewModel` 的子类。然而,如果您没有注意到或没有对上下文的生命周期做出反应,访问上下文可能是危险的。**建议的做法是避免处理在 ViewModels 中具有生命周期的对象。** -Let’s look at an example based on this issue in the tracker: **[Updating ViewModel on system locale change](https://issuetracker.google.com/issues/111961971).** +让我们看看跟踪器中基于此问题的示例:**[在系统区域设置更改时更新 ViewModel ](https://issuetracker.google.com/issues/111961971)。** ```Java -// Don't do this +// 别这么做 public class MyViewModel extends AndroidViewModel { public final MutableLiveData statusLabel = new MutableLiveData<>(); @@ -29,12 +28,12 @@ public class MyViewModel extends AndroidViewModel { } ``` -The problem is that the string is resolved in the constructor only once. **If there’s a locale change, the ViewModel won’t be recreated**. This will result in our app showing obsolete data and therefore being only partially localized. +问题的关键是字符串在构造器中只解释一次。**如果有区域设置更改,则不会重新创建视图模型**。这将导致我们的应用程序显示废弃的数据,因此只能部分本地化。 -As [Sergey](https://twitter.com/ZelenetS) points out in the [comments](https://issuetracker.google.com/issues/111961971#comment2) to the issue, the recommended approach is to **expose the ID of the resource you want to load and do so in the view**. As the view (activity, fragment, etc.) is lifecycle-aware it will be recreated after a configuration change so the resource will be reloaded correctly. +正如 [Sergey](https://twitter.com/ZelenetS) 在评论中指出的那样 [comments](https://issuetracker.google.com/issues/111961971#comment2),推荐的方法是**公开要加载的资源的 ID ,并在视图中这样做**。由于视图(活动、片段等)具有生命周期意识,因此它将在配置更改后重新创建,以便正确地重新加载资源。 ```Java -// Expose resource IDs instead +// 显示资源ID public class MyViewModel extends ViewModel { public final MutableLiveData statusLabel = new MutableLiveData<>(); @@ -45,9 +44,9 @@ public class MyViewModel extends ViewModel { } ``` -Even if you don’t plan to localize your app, it makes testing much easier and cleans up your ViewModel objects so there’s no reason not to future-proof. +即使你不打算本地化你的应用程序,它也会使测试变得更容易并且清空你的 ViewModel 对象,因此没有理由不去考虑它的前瞻性。 -We fixed this issue in the android-architecture repository in the [Java](https://github.com/googlesamples/android-architecture/pull/631) and [Kotlin](https://github.com/googlesamples/android-architecture/pull/635) branches and we offloaded resource loading to [the Data Binding layout](https://github.com/googlesamples/android-architecture/pull/635/files#diff-7eb5d85ec3ea4e05ecddb7dc8ae20aa1R62). +我们在以 Java 为基础的 Android 架构存储库中解决了这个问题 [Java](https://github.com/googlesamples/android-architecture/pull/631) 以及在[Kotlin](https://github.com/googlesamples/android-architecture/pull/635) 分支上。我们也把资源转移到 [数据绑定布局](https://github.com/googlesamples/android-architecture/pull/635/files#diff-7eb5d85ec3ea4e05ecddb7dc8ae20aa1R62)。 > 如果发现译文存在错误或其他需要改进的地方,欢迎到 [掘金翻译计划](https://github.com/xitu/gold-miner) 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 **本文永久链接** 即为本文在 GitHub 上的 MarkDown 链接。 From 647378d00b5d6b26f245f70b1cd4be0774c771ca Mon Sep 17 00:00:00 2001 From: LeviDing Date: Tue, 16 Jul 2019 20:15:13 +0800 Subject: [PATCH 49/68] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=B8=83=E6=9C=88?= =?UTF-8?q?=E4=BB=BD=E6=9D=82=E9=A1=B9=E7=A7=AF=E5=88=86=E5=92=8C=E7=A4=BC?= =?UTF-8?q?=E7=89=A9=E5=85=91=E6=8D=A2=E7=A7=AF=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- integrals.md | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/integrals.md b/integrals.md index 258f812dc5c..da55ba8a583 100644 --- a/integrals.md +++ b/integrals.md @@ -7521,6 +7521,7 @@ |文章|类型|积分| |------|-------|-------| +|2019 年 7 月兑小米台灯 1 个,掘金鼠标垫 1 个,掘金纪念币 2 个|减去积分|52| |[Xcode 和 LLDB 高级调试教程:第 1 部分](https://juejin.im/post/5d0b246be51d4555e372a60b)|校对|2| |[用于 iOS 的 ML Kit 教程:识别图像中的文字](https://juejin.im/post/5cfe23af6fb9a07ee742d401)|校对|2| |[iOS 中的 File Provider 拓展](https://juejin.im/post/5cff5b0af265da1b8b2b54c7)|翻译|9.5| @@ -8446,10 +8447,11 @@ |------|-------|-------| |[2019 版 web 浏览器现状](https://juejin.im/post/5c89e69a51882536fe67b5b4)|校对|3.5| -## 译者:[Xuyuey](https://github.com/Xuyuey) 历史贡献积分:85 当前积分:85 二零一九:85 +## 译者:[Xuyuey](https://github.com/Xuyuey) 历史贡献积分:89 当前积分:89 二零一九:89 |文章|类型|积分| |------|-------|-------| +|[The JavaScript Tutorial 教程](https://github.com/javascript-tutorial/zh.javascript.info)|翻译校对|4| |[JavaScript 简明代码 — 最佳实践](https://juejin.im/post/5d07abcc6fb9a07eda031858)|校对|1| |[如何用 React Hooks 打造一个不到 100 行代码的异步表单校验库](https://juejin.im/post/5cf4e2c2f265da1b80202f83)|校对|2| |[ECMAScript 类 - 定义私有属性](https://juejin.im/post/5d006e406fb9a07ed22465de)|校对|1.5| @@ -8836,10 +8838,12 @@ |------|-------|-------| |[The JavaScript Tutorial 教程](https://github.com/javascript-tutorial/zh.javascript.info)|翻译校对|3| -## 译者:[lycheeEng](https://github.com/lycheeEng) 历史贡献积分:13 当前积分:13 二零一九:13 +## 译者:[lycheeEng](https://github.com/lycheeEng) 历史贡献积分:53 当前积分:1 二零一九:53 |文章|类型|积分| |------|-------|-------| +|2019 年 6 月兑米家台灯,Google 鲁班锁和小袋子各 1 个|减去积分|52| +|[The JavaScript Tutorial 教程](https://github.com/javascript-tutorial/zh.javascript.info)|翻译校对|40| |[The JavaScript Tutorial 教程](https://github.com/javascript-tutorial/zh.javascript.info)|翻译校对|13| ## 译者:[smilemuffie](https://github.com/smilemuffie) 历史贡献积分:8 当前积分:8 二零一九:8 @@ -8870,10 +8874,12 @@ |[改善 Android Studio 的构建速度](https://juejin.im/post/5d1388b1f265da1b6d403560)|校对|1.5| |[揭秘变量提升](https://juejin.im/post/5d026b71518825710d2b1f63)|校对|1.5| -## 译者:[JalanJiang](https://github.com/JalanJiang) 历史贡献积分:18 当前积分:18 二零一九:18 +## 译者:[JalanJiang](https://github.com/JalanJiang) 历史贡献积分:23 当前积分:23 二零一九:23 |文章|类型|积分| |------|-------|-------| +|[The JavaScript Tutorial 教程](https://github.com/javascript-tutorial/zh.javascript.info)|翻译校对|4| +|推荐英文文章一篇|奖励|1| |推荐英文文章一篇|奖励|1| |[WebSockets 与长轮询的较量](https://juejin.im/post/5d0b1381e51d455a694f9544)|翻译|5| |[Python 实现排序算法](https://juejin.im/post/5d1323b6e51d45108b2caeaf)|校对|6.5| @@ -8951,3 +8957,21 @@ |文章|类型|积分| |------|-------|-------| |[The JavaScript Tutorial 教程](https://github.com/javascript-tutorial/zh.javascript.info)|翻译校对|13| + +## 译者:[MartinsYong](https://github.com/MartinsYong) 历史贡献积分:20 当前积分:20 二零一九:20 + +|文章|类型|积分| +|------|-------|-------| +|[The JavaScript Tutorial 教程](https://github.com/javascript-tutorial/zh.javascript.info)|翻译校对|20| + +## 译者:[scarqin](https://github.com/scarqin) 历史贡献积分:3 当前积分:3 二零一九:3 + +|文章|类型|积分| +|------|-------|-------| +|[The JavaScript Tutorial 教程](https://github.com/javascript-tutorial/zh.javascript.info)|翻译校对|3| + +## 译者:[savokiss](https://github.com/savokiss) 历史贡献积分:5 当前积分:5 二零一九:5 + +|文章|类型|积分| +|------|-------|-------| +|[The JavaScript Tutorial 教程](https://github.com/javascript-tutorial/zh.javascript.info)|翻译校对|5| From 5c7d92c6222d86ebef2e04c4256e9dd2a57cc279 Mon Sep 17 00:00:00 2001 From: LeviDing Date: Tue, 16 Jul 2019 20:27:49 +0800 Subject: [PATCH 50/68] =?UTF-8?q?fix=20=E7=A7=AF=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- integrals.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integrals.md b/integrals.md index da55ba8a583..9813c11fc06 100644 --- a/integrals.md +++ b/integrals.md @@ -7517,7 +7517,7 @@ |------|-------|-------| |[深入理解 React 高阶组件](https://juejin.im/entry/5bdd226cf265da616f6f6cce)|校对|4| -## 译者:[iWeslie](https://github.com/iWeslie) 历史贡献积分:132 当前积分:132 二零一九:88 +## 译者:[iWeslie](https://github.com/iWeslie) 历史贡献积分:132 当前积分:80 二零一九:88 |文章|类型|积分| |------|-------|-------| From 203ba75b88b55f35224b0917a924cd4408a07722 Mon Sep 17 00:00:00 2001 From: LeviDing Date: Tue, 16 Jul 2019 20:47:00 +0800 Subject: [PATCH 51/68] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=B8=83=E6=9C=88?= =?UTF-8?q?=E4=BB=BD=E9=83=A8=E5=88=86=E5=89=8D=E7=AB=AF=E5=88=86=E7=B1=BB?= =?UTF-8?q?=E6=96=87=E7=AB=A0=E7=BF=BB=E8=AF=91=E6=A0=A1=E5=AF=B9=E7=A7=AF?= =?UTF-8?q?=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- integrals.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/integrals.md b/integrals.md index 9813c11fc06..9b9ba0d8e12 100644 --- a/integrals.md +++ b/integrals.md @@ -2732,10 +2732,11 @@ |[在 Xcode 项目中使用 swift package fetch](https://juejin.im/entry/58c7a4cb1b69e6006bec354c/)|校对|1| |[Swift + 关键字](https://gold.xitu.io/entry/58bf76aaa22b9d0058896bff/)|校对|1| -## 译者:[xilihuasi](https://github.com/xilihuasi) 历史贡献积分:63 当前积分:63 二零一九:29 +## 译者:[xilihuasi](https://github.com/xilihuasi) 历史贡献积分:69 当前积分:69 二零一九:35 |文章|类型|积分| |------|-------|-------| +|[微前端:未来前端开发的新趋势 — 第三部分](https://juejin.im/post/5d2755c4e51d45105e021360)|翻译|6| |[JavaScript 简明代码 — 最佳实践](https://juejin.im/post/5d07abcc6fb9a07eda031858)|翻译|4| |[2019 React Redux 完全指南](https://juejin.im/post/5cac8ccd6fb9a068530111c7)|翻译|12.5| |[React Native 中那些令我收获颇丰的痛点](https://juejin.im/post/5c74d6a16fb9a049f06ae907)|校对|1.5| @@ -8808,10 +8809,12 @@ |[WebSockets 与长轮询的较量](https://juejin.im/post/5d0b1381e51d455a694f9544)|校对|1.5| |[如何在 Google Play 应用商店中发布 PWA](https://juejin.im/post/5cefe63a6fb9a07ef3764dbe)|校对|2| -## 译者:[lgh757079506](https://github.com/lgh757079506) 历史贡献积分:5 当前积分:5 二零一九:5 +## 译者:[lgh757079506](https://github.com/lgh757079506) 历史贡献积分:8.5 当前积分:8.5 二零一九:8.5 |文章|类型|积分| |------|-------|-------| +|[微前端:未来前端开发的新趋势 — 第三部分](https://juejin.im/post/5d2755c4e51d45105e021360)|校对|1.5| +|[微前端:未来前端开发的新趋势 — 第二部分](https://juejin.im/post/5d1a91c7e51d45775f516ab9)|校对|2| |[4 个 CSS 调色滤镜](https://juejin.im/post/5d039c36f265da1b60290096)|校对|1| |[JavaScript 线性代数:使用 ThreeJS 制作线性变换动画](https://juejin.im/post/5d05dba86fb9a07ece67ce76)|校对|1| |[ECMAScript 类 - 定义私有属性](https://juejin.im/post/5d006e406fb9a07ed22465de)|校对|1.5| @@ -8858,10 +8861,11 @@ |[Flutter 布局备忘录](https://juejin.im/post/5cfe0d136fb9a07efc497d7d)|校对|1.5| |[The JavaScript Tutorial 教程](https://github.com/javascript-tutorial/zh.javascript.info)|翻译校对|1.5| -## 译者:[lihaobhsfer](https://github.com/lihaobhsfer) 历史贡献积分:9.5 当前积分:9.5 二零一九:9.5 +## 译者:[lihaobhsfer](https://github.com/lihaobhsfer) 历史贡献积分:16 当前积分:16 二零一九:16 |文章|类型|积分| |------|-------|-------| +|[微前端:未来前端开发的新趋势 — 第二部分](https://juejin.im/post/5d1a91c7e51d45775f516ab9)|翻译|6.5| |[The JavaScript Tutorial 教程](https://github.com/javascript-tutorial/zh.javascript.info)|翻译校对|1.5| |[10 分钟爆改终端](https://juejin.im/post/5d053fc56fb9a07ee85c283d)|翻译|5| |[将第三方动画库集成到项目中 —— 第 1 部分](https://juejin.im/post/5d037b655188252af2012702)|校对|3| @@ -8893,10 +8897,12 @@ |------|-------|-------| |[如何用 React Hooks 打造一个不到 100 行代码的异步表单校验库](https://juejin.im/post/5cf4e2c2f265da1b80202f83)|校对|1.5| -## 译者:[Stevens1995](https://github.com/Stevens1995) 历史贡献积分:2 当前积分:2 二零一九:2 +## 译者:[Stevens1995](https://github.com/Stevens1995) 历史贡献积分:5.5 当前积分:5.5 二零一九:5.5 |文章|类型|积分| |------|-------|-------| +|[微前端:未来前端开发的新趋势 — 第三部分](https://juejin.im/post/5d2755c4e51d45105e021360)|校对|1.5| +|[微前端:未来前端开发的新趋势 — 第二部分](https://juejin.im/post/5d1a91c7e51d45775f516ab9)|校对|2| |[微前端:未来前端开发的新趋势 — 第一部分](https://juejin.im/post/5d0e367b6fb9a07ebf4b781a)|校对|1| |[JavaScript 线性代数:使用 ThreeJS 制作线性变换动画](https://juejin.im/post/5d05dba86fb9a07ece67ce76)|校对|1| From 8daa230861ebe8382dc57e88258b87bc37f7401d Mon Sep 17 00:00:00 2001 From: LeviDing Date: Tue, 16 Jul 2019 21:11:42 +0800 Subject: [PATCH 52/68] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=B8=83=E6=9C=88?= =?UTF-8?q?=E4=BB=BD=E5=89=8D=E7=AB=AF=E5=88=86=E7=B1=BB=E6=96=87=E7=AB=A0?= =?UTF-8?q?=E7=BF=BB=E8=AF=91=E6=A0=A1=E5=AF=B9=E7=A7=AF=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- integrals.md | 48 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/integrals.md b/integrals.md index 9b9ba0d8e12..b423d71cd69 100644 --- a/integrals.md +++ b/integrals.md @@ -4081,10 +4081,11 @@ |[在 HTTP/2 的世界里管理 CSS 和 JS](https://juejin.im/post/59bb463d51882519777c5a85)|校对|0.5| |[Coursera 的 GraphQL 之路](https://juejin.im/post/59b8d1d36fb9a00a3f24c439)|校对|1| -## 译者:[Usey95](https://github.com/Usey95) 历史贡献积分:44 当前积分:44 二零一九:8 +## 译者:[Usey95](https://github.com/Usey95) 历史贡献积分:50 当前积分:50 二零一九:14 |文章|类型|积分| |------|-------|-------| +|[微前端:未来前端开发的新趋势 — 第四部分](https://juejin.im/post/5d23394ae51d45778f076db0)|翻译|6| |[Git:透过命令学概念 —— 第一部分](https://juejin.im/post/5d0b3c7ce51d4577531381e3)|校对|2.5| |[JavaScript 中 JSON.stringify 的帕累托法则手册](https://juejin.im/post/5d11d8d4f265da1baf7cfa13)|校对|1| |[TypeScript 3.0: unknown 类型](https://juejin.im/post/5d04ac745188250a8b1fd203)|校对|1.5| @@ -4869,10 +4870,11 @@ |------|-------|-------| |[从 Gzip 压缩 SVG 说起 — 论如何减小资源文件的大小](https://juejin.im/post/5a30a7fdf265da4309452517)|校对|1| -## 译者:[noahziheng](https://github.com/noahziheng) 历史贡献积分:44 当前积分:26 +## 译者:[noahziheng](https://github.com/noahziheng) 历史贡献积分:46 当前积分:28 二零一九:2 |文章|类型|积分| |------|-------|-------| +|[自托管你的静态资源](https://juejin.im/post/5d258a77f265da1bca5202dc)|校对|2| |2019 年 3 月兑小黄鸭 1 个|减去积分|3| |[在 JSX 代码中可以加入 console.log 吗?](https://juejin.im/post/5c7b1a146fb9a049c8502caf)|校对|1| |[如何学习 CSS](https://juejin.im/post/5c74daaaf265da2d9d1cb774)|校对|2| @@ -6843,10 +6845,11 @@ |[The JavaScript Tutorial 翻译](https://github.com/xitu/javascript-tutorial-en)|翻译校对|3| |[The JavaScript Tutorial 翻译](https://github.com/xitu/javascript-tutorial-en)|翻译校对|1| -## 译者:[Moonliujk](https://github.com/Moonliujk) 历史贡献积分:77 当前积分:22 二零一九:12 +## 译者:[Moonliujk](https://github.com/Moonliujk) 历史贡献积分:81 当前积分:26 二零一九:16 |文章|类型|积分| |------|-------|-------| +|[使用 SVG 和 Vue.Js 构建动态树图](https://juejin.im/post/5d2806fb518825121c0058d8)|校对|4| |[从原型图到成品:步步深入 CSS 布局](https://juejin.im/post/5cebb52651882530be7b16a4)|校对|2| |[使用 closest() 函数获取正确的 DOM 元素](https://juejin.im/post/5cc811796fb9a0321c45e5e0)|校对|0.5| |[设计不会拯救世界](https://juejin.im/post/5c966aed5188252d9b3768df)|校对|2.5| @@ -6875,10 +6878,11 @@ |[让我们一起解决“this”难题  —  第二部分](https://juejin.im/post/5b6915cce51d4519962f0ca7)|校对|2| |[The JavaScript Tutorial 翻译](https://github.com/xitu/javascript-tutorial-en)|翻译校对|2.5| -## 译者:[xutaogit](https://github.com/xutaogit) 历史贡献积分:56 当前积分:49 二零一九:10 +## 译者:[xutaogit](https://github.com/xutaogit) 历史贡献积分:62 当前积分:55 二零一九:16 |文章|类型|积分| |------|-------|-------| +|[推广 PWA 安装的模式(移动端)](https://juejin.im/post/5d2746f1f265da1b7638cd1f)|翻译|6| |[Vue Router 实战手册](https://juejin.im/post/5c62ab05f265da2da83555a0)|翻译|5| |[我们采用 GraphQL 技术的经验:营销技术活动](https://juejin.im/post/5c4522566fb9a049d2365cd6)|翻译|5| |[程序构建系列教程简介](https://juejin.im/post/5c0dd214518825444758453a)|翻译|8| @@ -7055,10 +7059,11 @@ |[一行 JavaScript 代码竟然让 FT.com 网站慢了十倍](https://juejin.im/post/5b7bb6dfe51d4538bf55aa5f)|校对|1| |[使用 Web Beacon API 记录活动](https://juejin.im/post/5b694b5de51d4519700fa56a)|校对|1| -## 译者:[YueYongDev](https://github.com/YueYongDev) 历史贡献积分:53.5 当前积分:18.5 二零一九:24.5 +## 译者:[YueYongDev](https://github.com/YueYongDev) 历史贡献积分:59.5 当前积分:24.5 二零一九:30.5 |文章|类型|积分| |------|-------|-------| +|[使用 SVG 和 Vue.Js 构建动态树图](https://juejin.im/post/5d2806fb518825121c0058d8)|翻译|6| |[在 Android 应用中使用矢量资源](https://juejin.im/post/5c943c97e51d45288201a30c)|翻译|3| |2019 年 3 月兑米家 LED 智能台灯 1 个|减去积分|35| |[HTTP/2 常见问题解答](https://juejin.im/post/5c5ada2e6fb9a049dd80be75)|翻译|8.5| @@ -8012,10 +8017,11 @@ |[💅 styled-components 背后的魔力](https://juejin.im/post/5c6d3a32e51d451804732248)|翻译|3| |推荐英文文章一篇|奖励|1| -## 译者:[shixi-li](https://github.com/shixi-li) 历史贡献积分:57.5 当前积分:57.5 二零一九:57.5 +## 译者:[shixi-li](https://github.com/shixi-li) 历史贡献积分:59 当前积分:59 二零一九:59 |文章|类型|积分| |------|-------|-------| +|[使用 SVG 和 Vue.Js 构建动态树图](https://juejin.im/post/5d2806fb518825121c0058d8)|校对|1.5| |[TypeScript 3.0: unknown 类型](https://juejin.im/post/5d04ac745188250a8b1fd203)|翻译|4| |[线性代数:矩阵基本运算](https://juejin.im/post/5d107b00f265da1b67211a21)|校对|1| |[浅析深度学习神经网络的卷积层](https://juejin.im/post/5ceeef01518825351e354747)|校对|2| @@ -8677,10 +8683,11 @@ |[自动补全规则](https://juejin.im/post/5cd556ef6fb9a03218556cb7)|翻译|3| |[使用 PyTorch 在 MNIST 数据集上进行逻辑回归](https://juejin.im/post/5cc66d946fb9a032286173a7)|校对|1| -## 译者:[Chorer](https://github.com/Chorer) 历史贡献积分:9 当前积分:9 二零一九:9 +## 译者:[Chorer](https://github.com/Chorer) 历史贡献积分:10.5 当前积分:10.5 二零一九:10.5 |文章|类型|积分| |------|-------|-------| +|[CSS 思维模式](https://juejin.im/post/5d295380f265da1bab29dc13)|校对|1.5| |推荐英文文章一篇|奖励|1| |[Commit 提交指南](https://juejin.im/post/5ccf9e60f265da039c05659d)|校对|2.5| |推荐英文文章一篇|奖励|1| @@ -8765,10 +8772,11 @@ |[超快速的分析器(一):优化扫描器](https://juejin.im/post/5ce8cbd9e51d4556bb4cd2f9)|校对|2| |推荐英文文章一篇|奖励|1| -## 译者:[wuyanan](https://github.com/wuyanan) 历史贡献积分:4 当前积分:4 二零一九:4 +## 译者:[wuyanan](https://github.com/wuyanan) 历史贡献积分:5.5 当前积分:5.5 二零一九:5.5 |文章|类型|积分| |------|-------|-------| +|[推广 PWA 安装的模式(移动端)](https://juejin.im/post/5d2746f1f265da1b7638cd1f)|校对|1.5| |[为什么我们要切换到 gRPC](https://juejin.im/post/5cff855c518825612f412526)|校对|1| |[用 React 的钩子函数和调试工具提升应用性能](https://juejin.im/post/5ce974d76fb9a07f0420250e)|校对|1| |[理解 WebView](https://juejin.im/post/5ce76ee4f265da1b8d15f700)|校对|2| @@ -8780,10 +8788,11 @@ |[理解 Vue.js 中的 Mixins](https://juejin.im/post/5cdeac5851882525f52cf662)|校对|1| |[为什么 HTML 中复选框样式难写 — 本文给你答案](https://juejin.im/post/5ce65dd36fb9a07ef06f6cd2)|翻译|4| -## 译者:[twang1727](https://github.com/twang1727) 历史贡献积分:14.5 当前积分:14.5 二零一九:14.5 +## 译者:[twang1727](https://github.com/twang1727) 历史贡献积分:20 当前积分:20 二零一九:20 |文章|类型|积分| |------|-------|-------| +|[自托管你的静态资源](https://juejin.im/post/5d258a77f265da1bca5202dc)|翻译|5.5| |[The JavaScript Tutorial 教程](https://github.com/javascript-tutorial/zh.javascript.info)|翻译校对|2| |[通过 Rust 学习解析器组合器 — 第二部分](https://juejin.im/post/5d04c3abe51d45775d516f7b)|校对|2| |[利用并行流渐进加载图片](https://juejin.im/post/5d044fe3f265da1bd04eddda)|翻译|4.5| @@ -8807,12 +8816,14 @@ |文章|类型|积分| |------|-------|-------| |[WebSockets 与长轮询的较量](https://juejin.im/post/5d0b1381e51d455a694f9544)|校对|1.5| -|[如何在 Google Play 应用商店中发布 PWA](https://juejin.im/post/5cefe63a6fb9a07ef3764dbe)|校对|2| +|[如何在 Google Play 应用商店中发布 PWA](https://juejin.im/post/5cefe63a6fb9a07ef3764dbe)|校对| -## 译者:[lgh757079506](https://github.com/lgh757079506) 历史贡献积分:8.5 当前积分:8.5 二零一九:8.5 +## 译者:[lgh757079506](https://github.com/lgh757079506) 历史贡献积分:11.5 当前积分:11.5 二零一九:11.5 |文章|类型|积分| |------|-------|-------| +|[推广 PWA 安装的模式(移动端)](https://juejin.im/post/5d2746f1f265da1b7638cd1f)|校对|1.5| +|[微前端:未来前端开发的新趋势 — 第四部分](https://juejin.im/post/5d23394ae51d45778f076db0)|校对|1.5| |[微前端:未来前端开发的新趋势 — 第三部分](https://juejin.im/post/5d2755c4e51d45105e021360)|校对|1.5| |[微前端:未来前端开发的新趋势 — 第二部分](https://juejin.im/post/5d1a91c7e51d45775f516ab9)|校对|2| |[4 个 CSS 调色滤镜](https://juejin.im/post/5d039c36f265da1b60290096)|校对|1| @@ -8849,10 +8860,11 @@ |[The JavaScript Tutorial 教程](https://github.com/javascript-tutorial/zh.javascript.info)|翻译校对|40| |[The JavaScript Tutorial 教程](https://github.com/javascript-tutorial/zh.javascript.info)|翻译校对|13| -## 译者:[smilemuffie](https://github.com/smilemuffie) 历史贡献积分:8 当前积分:8 二零一九:8 +## 译者:[smilemuffie](https://github.com/smilemuffie) 历史贡献积分:10 当前积分:10 二零一九:10 |文章|类型|积分| |------|-------|-------| +|[Web 流式文字排版的现状](https://juejin.im/post/5d267d9de51d45773d4686ab)|校对|2| |[TypeScript 3.0: unknown 类型](https://juejin.im/post/5d04ac745188250a8b1fd203)|校对|1| |[JavaScript 简明代码 — 最佳实践](https://juejin.im/post/5d07abcc6fb9a07eda031858)|校对|1| |[13 种用于 DOM 操作的 JavaScript 方法](https://juejin.im/post/5cf65369f265da1bc94edad8)|校对|0.5| @@ -8915,16 +8927,19 @@ |[在 npm 上启用现在 JavaScript](https://juejin.im/post/5d15d64be51d4510a5033603)|校对|2| |[理解 React 中的高阶组件](https://juejin.im/post/5d1037966fb9a07ee4636de3)|校对|1| -## 译者:[Jenniferyingni](https://github.com/Jenniferyingni) 历史贡献积分:4 当前积分:4 二零一九:4 +## 译者:[Jenniferyingni](https://github.com/Jenniferyingni) 历史贡献积分:1 当前积分:11 二零一九:11 |文章|类型|积分| |------|-------|-------| +|[Web 流式文字排版的现状](https://juejin.im/post/5d267d9de51d45773d4686ab)|翻译|7| |[微前端:未来前端开发的新趋势 — 第一部分](https://juejin.im/post/5d0e367b6fb9a07ebf4b781a)|翻译|4| -## 译者:[MarchYuanx](https://github.com/MarchYuanx) 历史贡献积分:3.5 当前积分:3.5 二零一九:3.5 +## 译者:[MarchYuanx](https://github.com/MarchYuanx) 历史贡献积分:9 当前积分:9 二零一九:9 |文章|类型|积分| |------|-------|-------| +|[CSS 思维模式](https://juejin.im/post/5d295380f265da1bab29dc13)|翻译|3.5| +|[自托管你的静态资源](https://juejin.im/post/5d258a77f265da1bca5202dc)|校对|2| |[设计师如何成长为 Leader?](https://juejin.im/post/5d172fca6fb9a07eda032c6f)|校对|2| |[在 npm 上启用现在 JavaScript](https://juejin.im/post/5d15d64be51d4510a5033603)|校对|1.5| @@ -8932,6 +8947,7 @@ |文章|类型|积分| |------|-------|-------| +|[Web 流式文字排版的现状](https://juejin.im/post/5d267d9de51d45773d4686ab)|校对|4| |[npm 的经济风云 —— 上半部分](https://juejin.im/post/5d146225e51d4556db694a4b)|校对|2| ## 译者:[krircc](https://github.com/krircc) 历史贡献积分:1 当前积分:1 二零一九:1 @@ -8946,10 +8962,12 @@ |------|-------|-------| |[The JavaScript Tutorial 教程](https://github.com/javascript-tutorial/zh.javascript.info)|翻译校对|3| -## 译者:[solerji](https://github.com/solerji) 历史贡献积分:2 当前积分:2 二零一九:2 +## 译者:[solerji](https://github.com/solerji) 历史贡献积分:4 当前积分:4 二零一九4 |文章|类型|积分| |------|-------|-------| +|[CSS 思维模式](https://juejin.im/post/5d295380f265da1bab29dc13)|校对|0.5| +|[微前端:未来前端开发的新趋势 — 第四部分](https://juejin.im/post/5d23394ae51d45778f076db0)|校对|1.5| |[The JavaScript Tutorial 教程](https://github.com/javascript-tutorial/zh.javascript.info)|翻译校对|2| ## 译者:[Amabel](https://github.com/Amabel) 历史贡献积分:1 当前积分:1 二零一九:1 From d33f26e425105f70a960fe0b8df14a44710e4dd9 Mon Sep 17 00:00:00 2001 From: LeviDing Date: Tue, 16 Jul 2019 21:11:46 +0800 Subject: [PATCH 53/68] Update front-end.md --- front-end.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/front-end.md b/front-end.md index 2f0c5cc4435..5b19780f80c 100644 --- a/front-end.md +++ b/front-end.md @@ -1,8 +1,16 @@ +* [CSS 思维模式](https://juejin.im/post/5d295380f265da1bab29dc13) ([MarchYuanx](https://github.com/MarchYuanx) 翻译) +* [使用 SVG 和 Vue.Js 构建动态树图](https://juejin.im/post/5d2806fb518825121c0058d8) ([YueYongDev](https://github.com/YueYongDev) 翻译) +* [推广 PWA 安装的模式(移动端)](https://juejin.im/post/5d2746f1f265da1b7638cd1f) ([xutaogit](https://github.com/xutaogit) 翻译) +* [Web 流式文字排版的现状](https://juejin.im/post/5d267d9de51d45773d4686ab) ([Jenniferyingni](https://github.com/Jenniferyingni) 翻译) +* [自托管你的静态资源](https://juejin.im/post/5d258a77f265da1bca5202dc) ([twang1727](https://github.com/twang1727) 翻译) +* [微前端:未来前端开发的新趋势 — 第四部分](https://juejin.im/post/5d23394ae51d45778f076db0) ([Usey95](https://github.com/Usey95) 翻译) +* [微前端:未来前端开发的新趋势 — 第三部分](https://juejin.im/post/5d2755c4e51d45105e021360) ([xilihuasi](https://github.com/xilihuasi) 翻译) +* [微前端:未来前端开发的新趋势 — 第二部分](https://juejin.im/post/5d1a91c7e51d45775f516ab9) ([lihaobhsfer](https://github.com/lihaobhsfer) 翻译) +* [微前端:未来前端开发的新趋势 — 第一部分](https://juejin.im/post/5d0e367b6fb9a07ebf4b781a) ([Jenniferyingni](https://github.com/Jenniferyingni) 翻译) * [理解 React 中的高阶组件](https://juejin.im/post/5d1037966fb9a07ee4636de3) ([ZavierTang](https://github.com/ZavierTang) 翻译) * [JavaScript 简明代码 — 最佳实践](https://juejin.im/post/5d07abcc6fb9a07eda031858) ([xilihuasi](https://github.com/xilihuasi) 翻译) * [4 个 CSS 调色滤镜](https://juejin.im/post/5d039c36f265da1b60290096) ([iceytea](https://github.com/iceytea) 翻译) * [TypeScript 3.0: unknown 类型](https://juejin.im/post/5d04ac745188250a8b1fd203) ([shixi-li](https://github.com/shixi-li) 翻译) -* [微前端:未来前端开发的新趋势 — 第一部分](https://juejin.im/post/5d0e367b6fb9a07ebf4b781a) ([Jenniferyingni](https://github.com/Jenniferyingni) 翻译) * [在 npm 上启用现在 JavaScript](https://juejin.im/post/5d15d64be51d4510a5033603) ([Mirosalva](https://github.com/Mirosalva) 翻译) * [npm 的经济风云 —— 上半部分](https://juejin.im/post/5d146225e51d4556db694a4b) ([Baddyo](https://github.com/Baddyo) 翻译) * [JavaScript 中 JSON.stringify 的帕累托法则手册](https://juejin.im/post/5d11d8d4f265da1baf7cfa13) ([Jerry-FD](https://github.com/Jerry-FD) 翻译) From a8b10c06b76d06d264d7746d86a9ac6908000066 Mon Sep 17 00:00:00 2001 From: LeviDing Date: Tue, 16 Jul 2019 21:19:13 +0800 Subject: [PATCH 54/68] Update backend.md --- backend.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/backend.md b/backend.md index 7f9de4399e6..cfd16356d62 100644 --- a/backend.md +++ b/backend.md @@ -1,11 +1,14 @@ +* [Kubernetes 儿童插图指南](https://juejin.im/post/5d1b2a656fb9a07edc0b7058) ([JalanJiang](https://github.com/JalanJiang) 翻译) +* [使用 Gomobile 和 Gopherjs 的动态二维码数据传输](https://juejin.im/post/5d2bfccef265da1bb77699e8) ([EmilyQiRabbit](https://github.com/EmilyQiRabbit) 翻译) +* [通过 Rust 学习解析器组合器 — 第三部分](https://juejin.im/post/5d1f29f7f265da1b961324b2) ([suhanyujie](https://github.com/suhanyujie) 翻译) +* [通过 Rust 学习解析器组合器 — 第二部分](https://juejin.im/post/5d04c3abe51d45775d516f7b) ([suhanyujie](https://github.com/suhanyujie) 翻译) +* [通过 Rust 学习解析器组合器 — 第一部分](https://juejin.im/post/5cfddd00f265da1b7e102bee) ([suhanyujie](https://github.com/suhanyujie) 翻译) * [Python 实现排序算法](https://juejin.im/post/5d1323b6e51d45108b2caeaf) ([fireairforce](https://github.com/fireairforce) 翻译) * [WebSockets 与长轮询的较量](https://juejin.im/post/5d0b1381e51d455a694f9544) ([JalanJiang](https://github.com/JalanJiang) 翻译) * [可维护的 ETL: 使管道更容易支持和扩展的技巧](https://juejin.im/post/5d08e178518825166f36bf89) ([fireairforce](https://github.com/fireairforce) 翻译) * [利用并行流渐进加载图片](https://juejin.im/post/5d044fe3f265da1bd04eddda) ([twang1727](https://github.com/twang1727) 翻译) -* [通过 Rust 学习解析器组合器 — 第二部分](https://juejin.im/post/5d04c3abe51d45775d516f7b) ([suhanyujie](https://github.com/suhanyujie) 翻译) * [尝试 DevOps:最适合你的是什么样的工具?](https://juejin.im/post/5cfd4aa3f265da1bb277233e) ([Starriers](https://github.com/Starriers) 翻译) * [超快速的分析器(二):惰性解析](https://juejin.im/post/5cf33bd751882579e53f0130) ([suhanyujie](https://github.com/suhanyujie) 翻译) -* [通过 Rust 学习解析器组合器 — 第一部分](https://juejin.im/post/5cfddd00f265da1b7e102bee) ([suhanyujie](https://github.com/suhanyujie) 翻译) * [如何使用 Node.js 构建一个命令行界面(CLI)](https://juejin.im/post/5cf2111b5188250d2850f884) ([EmilyQiRabbit](https://github.com/EmilyQiRabbit) 翻译) * [Node.js 日志记录指南](https://juejin.im/post/5cf213e4e51d4577407b1cda) ([fireairforce](https://github.com/fireairforce) 翻译) * [超快速的分析器(一):优化扫描器](https://juejin.im/post/5ce8cbd9e51d4556bb4cd2f9) ([nettee](https://github.com/nettee) 翻译) From a04626408dc2e8c2311452d132bf6a7f763eeea0 Mon Sep 17 00:00:00 2001 From: LeviDing Date: Tue, 16 Jul 2019 21:32:48 +0800 Subject: [PATCH 55/68] =?UTF-8?q?fix=20=E7=A7=AF=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- integrals.md | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/integrals.md b/integrals.md index b423d71cd69..b46c7bbc77c 100644 --- a/integrals.md +++ b/integrals.md @@ -5910,10 +5910,11 @@ |[让 Apache Cassandra 尾部延迟减小 10 倍(已开源)](https://juejin.im/post/5ac31083f265da239a5fff0c)|翻译|4| |[让我们来简化 UserDefaults 的使用](https://juejin.im/post/5abde324f265da23826e1723)|校对|0.5| -## 译者:[EmilyQiRabbit](https://github.com/EmilyQiRabbit) 历史贡献积分:160 当前积分:140二零一九:78 +## 译者:[EmilyQiRabbit](https://github.com/EmilyQiRabbit) 历史贡献积分:168 当前积分:148 二零一九:86 |文章|类型|积分| |------|-------|-------| +|[使用 Gomobile 和 Gopherjs 的动态二维码数据传输](https://juejin.im/post/5d2bfccef265da1bb77699e8)|翻译|8| |[类(Class)与数据结构(Data Structures)](https://juejin.im/post/5d12efe7e51d455c8838e193)|翻译|4| |[为什么我们要切换到 gRPC](https://juejin.im/post/5cff855c518825612f412526)|翻译|3.5| |[Flutter 布局备忘录](https://juejin.im/post/5cfe0d136fb9a07efc497d7d)|翻译|3| @@ -8754,10 +8755,12 @@ |[声音设计与无声设计](https://juejin.im/post/5ce354bee51d4510727c7fd1)|翻译|2+0.5| |[Swift 里的强制 @inline 注解](https://juejin.im/post/5cd67d64518825686244635a)|校对|1.5| -## 译者:[suhanyujie](https://github.com/suhanyujie) 历史贡献积分:36 当前积分:36 二零一九:36 +## 译者:[suhanyujie](https://github.com/suhanyujie) 历史贡献积分:45 当前积分:45 二零一九:45 |文章|类型|积分| |------|-------|-------| +|[使用 Gomobile 和 Gopherjs 的动态二维码数据传输](https://juejin.im/post/5d2bfccef265da1bb77699e8)|校对|3.5| +|[通过 Rust 学习解析器组合器 — 第三部分](https://juejin.im/post/5d1f29f7f265da1b961324b2)|翻译|5.5| |推荐英文文章一篇|奖励|1| |[通过 Rust 学习解析器组合器 — 第二部分](https://juejin.im/post/5d04c3abe51d45775d516f7b)|翻译|5| |[利用并行流渐进加载图片](https://juejin.im/post/5d044fe3f265da1bd04eddda)|校对|2| @@ -8816,7 +8819,7 @@ |文章|类型|积分| |------|-------|-------| |[WebSockets 与长轮询的较量](https://juejin.im/post/5d0b1381e51d455a694f9544)|校对|1.5| -|[如何在 Google Play 应用商店中发布 PWA](https://juejin.im/post/5cefe63a6fb9a07ef3764dbe)|校对| +|[如何在 Google Play 应用商店中发布 PWA](https://juejin.im/post/5cefe63a6fb9a07ef3764dbe)|校对|2| ## 译者:[lgh757079506](https://github.com/lgh757079506) 历史贡献积分:11.5 当前积分:11.5 二零一九:11.5 @@ -8882,18 +8885,20 @@ |[10 分钟爆改终端](https://juejin.im/post/5d053fc56fb9a07ee85c283d)|翻译|5| |[将第三方动画库集成到项目中 —— 第 1 部分](https://juejin.im/post/5d037b655188252af2012702)|校对|3| -## 译者:[csming1995](https://github.com/csming1995) 历史贡献积分:5 当前积分:5 二零一九:5 +## 译者:[csming1995](https://github.com/csming1995) 历史贡献积分:6 当前积分:6 二零一九:6 |文章|类型|积分| |------|-------|-------| +|[Kubernetes 儿童插图指南](https://juejin.im/post/5d1b2a656fb9a07edc0b7058)|校对|1| |[Python 实现排序算法](https://juejin.im/post/5d1323b6e51d45108b2caeaf)|校对|2| |[改善 Android Studio 的构建速度](https://juejin.im/post/5d1388b1f265da1b6d403560)|校对|1.5| |[揭秘变量提升](https://juejin.im/post/5d026b71518825710d2b1f63)|校对|1.5| -## 译者:[JalanJiang](https://github.com/JalanJiang) 历史贡献积分:23 当前积分:23 二零一九:23 +## 译者:[JalanJiang](https://github.com/JalanJiang) 历史贡献积分:26 当前积分:26 二零一九:26 |文章|类型|积分| |------|-------|-------| +|[Kubernetes 儿童插图指南](https://juejin.im/post/5d1b2a656fb9a07edc0b7058)|翻译|3| |[The JavaScript Tutorial 教程](https://github.com/javascript-tutorial/zh.javascript.info)|翻译校对|4| |推荐英文文章一篇|奖励|1| |推荐英文文章一篇|奖励|1| @@ -8943,10 +8948,11 @@ |[设计师如何成长为 Leader?](https://juejin.im/post/5d172fca6fb9a07eda032c6f)|校对|2| |[在 npm 上启用现在 JavaScript](https://juejin.im/post/5d15d64be51d4510a5033603)|校对|1.5| -## 译者:[mymmon](https://github.com/mymmon) 历史贡献积分:2 当前积分:2 二零一九:2 +## 译者:[mymmon](https://github.com/mymmon) 历史贡献积分:7 当前积分:7 二零一九:7 |文章|类型|积分| |------|-------|-------| +|[Kubernetes 儿童插图指南](https://juejin.im/post/5d1b2a656fb9a07edc0b7058)|校对|1| |[Web 流式文字排版的现状](https://juejin.im/post/5d267d9de51d45773d4686ab)|校对|4| |[npm 的经济风云 —— 上半部分](https://juejin.im/post/5d146225e51d4556db694a4b)|校对|2| @@ -8962,7 +8968,7 @@ |------|-------|-------| |[The JavaScript Tutorial 教程](https://github.com/javascript-tutorial/zh.javascript.info)|翻译校对|3| -## 译者:[solerji](https://github.com/solerji) 历史贡献积分:4 当前积分:4 二零一九4 +## 译者:[solerji](https://github.com/solerji) 历史贡献积分:4 当前积分:4 二零一九:4 |文章|类型|积分| |------|-------|-------| From 75d8c0ef478405a5f8029bee32760b694d326aa6 Mon Sep 17 00:00:00 2001 From: LeviDing Date: Tue, 16 Jul 2019 21:44:22 +0800 Subject: [PATCH 56/68] Update ios.md --- ios.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ios.md b/ios.md index 68075974030..0d1605b2bdf 100644 --- a/ios.md +++ b/ios.md @@ -1,3 +1,5 @@ +* [使用 Swift 5 构建一个 iOS 移动端群聊应用程序](https://juejin.im/post/5d2c6e846fb9a07ebb0564ae) ([LucaslEliane](https://github.com/LucaslEliane) 翻译) +* [Xcode 和 LLDB 高级调试教程:第 2 部分](https://juejin.im/post/5d2321eee51d454f71439d64) ([kirinzer](https://github.com/kirinzer) 翻译) * [Xcode 和 LLDB 高级调试教程:第 1 部分](https://juejin.im/post/5d0b246be51d4555e372a60b) ([kirinzer](https://github.com/kirinzer) 翻译) * [iOS 中的 File Provider 拓展](https://juejin.im/post/5cff5b0af265da1b8b2b54c7) ([iWeslie](https://github.com/iWeslie) 翻译) * [用于 iOS 的 ML Kit 教程:识别图像中的文字](https://juejin.im/post/5cfe23af6fb9a07ee742d401) ([portandbridge](https://github.com/portandbridge) 翻译) From 2d619b6db4d72363d1ff04b75527efc9c8284925 Mon Sep 17 00:00:00 2001 From: LeviDing Date: Tue, 16 Jul 2019 21:58:11 +0800 Subject: [PATCH 57/68] Update ios.md --- ios.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ios.md b/ios.md index 0d1605b2bdf..4d9ce52fec2 100644 --- a/ios.md +++ b/ios.md @@ -1,3 +1,5 @@ +* [贫困线下的软件 — 开源项目的可持续发展问题探讨](https://juejin.im/post/5d215da2e51d4556be5b3acb) ([Charlo-O](https://github.com/Charlo-O) 翻译) +* [Git:透过命令学概念 —— 第二部分](https://juejin.im/post/5d2da05ae51d45106b15ffca) ([Mirosalva](https://github.com/Mirosalva) 翻译) * [使用 Swift 5 构建一个 iOS 移动端群聊应用程序](https://juejin.im/post/5d2c6e846fb9a07ebb0564ae) ([LucaslEliane](https://github.com/LucaslEliane) 翻译) * [Xcode 和 LLDB 高级调试教程:第 2 部分](https://juejin.im/post/5d2321eee51d454f71439d64) ([kirinzer](https://github.com/kirinzer) 翻译) * [Xcode 和 LLDB 高级调试教程:第 1 部分](https://juejin.im/post/5d0b246be51d4555e372a60b) ([kirinzer](https://github.com/kirinzer) 翻译) From 5a050b86ab142e94dbf4ff9c49765e736bbf85e9 Mon Sep 17 00:00:00 2001 From: LeviDing Date: Tue, 16 Jul 2019 22:03:07 +0800 Subject: [PATCH 58/68] Update AI.md --- AI.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/AI.md b/AI.md index 294e48748d7..7d5cbc01a00 100644 --- a/AI.md +++ b/AI.md @@ -1,3 +1,5 @@ +* [时间序列数据间量化同步的四种方法](https://juejin.im/post/5d213c126fb9a07f091bc3f5) ([EmilyQiRabbit](https://github.com/EmilyQiRabbit) 翻译) +* [在 Python 中过度使用列表解析器和生成表达式](https://juejin.im/post/5d281b0ff265da1b8b2b8ae0) ([ccJia](https://github.com/ccJia) 翻译) * [使用 What-If 工具来研究机器学习模型](https://juejin.im/post/5d143abff265da1bb80c4005) ([Starriers](https://github.com/Starriers) 翻译) * [如何在 Keras 中用 YOLOv3 进行对象检测](https://juejin.im/post/5d12eef5e51d455a68490ba8) ([Daltan](https://github.com/Daltan) 翻译) * [在机器学习中为什么要进行 One-Hot 编码?](https://juejin.im/post/5d15840e5188255c23553204) ([lsvih](https://github.com/lsvih) 翻译) From bea33380fde048916b98bd02333f19b8cf107b7a Mon Sep 17 00:00:00 2001 From: LeviDing Date: Tue, 16 Jul 2019 22:06:03 +0800 Subject: [PATCH 59/68] Update android.md --- android.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/android.md b/android.md index 287ad992c45..49c6ac2e5c5 100644 --- a/android.md +++ b/android.md @@ -1,3 +1,5 @@ +* [区域设置更改和 AndroidViewModel 反面模式](https://juejin.im/post/5d2db596f265da1b5f2688d3) ([solerji](https://github.com/solerji) 翻译) +* [思考实践:用 Go 实现 Flutter](https://juejin.im/post/5d215b8df265da1b7b31ac8f) ([suhanyujie](https://github.com/suhanyujie) 翻译) * [Android 数据绑定库  — 从可观察域到 LiveData 仅需两步](https://juejin.im/post/5d12d76cf265da1b8b2b6d6e) ([gs666](https://github.com/gs666) 翻译) * [改善 Android Studio 的构建速度](https://juejin.im/post/5d1388b1f265da1b6d403560) ([qiuyuezhong](https://github.com/qiuyuezhong) 翻译) * [Android中的简易协程:viewModelScope](https://juejin.im/post/5cf35ec76fb9a07ed657bbcd) ([twang1727](https://github.com/twang1727) 翻译) From b9e838fdd3b435a03e05c1938b2e3db44623f0b1 Mon Sep 17 00:00:00 2001 From: LeviDing Date: Tue, 16 Jul 2019 22:08:34 +0800 Subject: [PATCH 60/68] Update product.md --- product.md | 1 + 1 file changed, 1 insertion(+) diff --git a/product.md b/product.md index 376e20aa12f..2ff84cfde20 100644 --- a/product.md +++ b/product.md @@ -1,3 +1,4 @@ +* [利用 84 种认知偏见设计更好的产品 —— 第一部分](https://juejin.im/post/5d2acf995188254c1915bd12) ([JalanJiang](https://github.com/JalanJiang) 翻译) * [制定良好的路线图:产品负责人的六个实施步骤](https://juejin.im/post/5cb299436fb9a068744e70a7) ([QiaoN](https://github.com/QiaoN) 翻译) * [2019 版 web 浏览器现状](https://juejin.im/post/5c89e69a51882536fe67b5b4) ([xionglong58](https://github.com/xionglong58) 翻译) * [产品管理思维模式适合每一个人](https://juejin.im/post/5c2c266ae51d4511fb7db0c7) ([EmilyQiRabbit](https://github.com/EmilyQiRabbit) 翻译) From 586b62cb6552effb39421fe16c0fe0f7bbbd24e7 Mon Sep 17 00:00:00 2001 From: LeviDing Date: Tue, 16 Jul 2019 22:11:01 +0800 Subject: [PATCH 61/68] Update design.md --- design.md | 1 + 1 file changed, 1 insertion(+) diff --git a/design.md b/design.md index 80318ba5087..59ff7027a55 100644 --- a/design.md +++ b/design.md @@ -1,3 +1,4 @@ +* [设计任何图表的六项原则](https://juejin.im/post/5d27fca7f265da1b5e731f92) ([MarchYuanx](https://github.com/MarchYuanx) 翻译) * [感受 4px 基线网格带来的便利](https://juejin.im/post/5d09e5ecf265da1b60290798) ([Mcskiller](https://github.com/Mcskiller) 翻译) * [设计师如何成长为 Leader?](https://juejin.im/post/5d172fca6fb9a07eda032c6f) ([TiaossuP](https://github.com/TiaossuP) 翻译) * [微设计系统 — 打破藩篱](https://juejin.im/post/5d0335395188255ee806a5da) ([Charlo-O](https://github.com/Charlo-O) 翻译) From 0ff7fad29eda7603374a29a616e451ba03fe156f Mon Sep 17 00:00:00 2001 From: LeviDing Date: Tue, 16 Jul 2019 22:11:14 +0800 Subject: [PATCH 62/68] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=B8=83=E6=9C=88?= =?UTF-8?q?=E4=B8=8A=E5=8D=8A=E6=9C=88=E5=85=B6=E4=BB=96=E6=96=87=E7=AB=A0?= =?UTF-8?q?=E7=BF=BB=E8=AF=91=E6=A0=A1=E5=AF=B9=E7=A7=AF=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- integrals.md | 71 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 53 insertions(+), 18 deletions(-) diff --git a/integrals.md b/integrals.md index b46c7bbc77c..d6b5bd586b9 100644 --- a/integrals.md +++ b/integrals.md @@ -5662,10 +5662,11 @@ |[json — JavaScript 对象表示法](https://juejin.im/post/5a9432ae5188257a5c6092b0)|校对|1| |[嵌套三元表达式棒极了(软件编写)(第十四部分)](https://juejin.im/post/5a7d6769f265da4e7e10ad82)|校对|1| -## 译者:[zhmhhu](https://github.com/zhmhhu) 历史贡献积分:118.5 当前积分:18.5 二零一九:13 +## 译者:[zhmhhu](https://github.com/zhmhhu) 历史贡献积分:120 当前积分:20 二零一九:14.5 |文章|类型|积分| |------|-------|-------| +|[时间序列数据间量化同步的四种方法](https://juejin.im/post/5d213c126fb9a07f091bc3f5)|校对|1.5| |[如何在 Keras 中用 YOLOv3 进行对象检测](https://juejin.im/post/5d12eef5e51d455a68490ba8)|校对|4| |[Python 架构相关:我们需要更多吗?](https://juejin.im/post/5cd1db8c51882535b323a3c7)|校对|1| |[使用 Python Flask 框架发布机器学习 API](https://juejin.im/post/5cd7f862e51d453aa44ad6f3)|校对|1| @@ -5910,10 +5911,11 @@ |[让 Apache Cassandra 尾部延迟减小 10 倍(已开源)](https://juejin.im/post/5ac31083f265da239a5fff0c)|翻译|4| |[让我们来简化 UserDefaults 的使用](https://juejin.im/post/5abde324f265da23826e1723)|校对|0.5| -## 译者:[EmilyQiRabbit](https://github.com/EmilyQiRabbit) 历史贡献积分:168 当前积分:148 二零一九:86 +## 译者:[EmilyQiRabbit](https://github.com/EmilyQiRabbit) 历史贡献积分:172 当前积分:152 二零一九:90 |文章|类型|积分| |------|-------|-------| +|[时间序列数据间量化同步的四种方法](https://juejin.im/post/5d213c126fb9a07f091bc3f5)|翻译|4| |[使用 Gomobile 和 Gopherjs 的动态二维码数据传输](https://juejin.im/post/5d2bfccef265da1bb77699e8)|翻译|8| |[类(Class)与数据结构(Data Structures)](https://juejin.im/post/5d12efe7e51d455c8838e193)|翻译|4| |[为什么我们要切换到 gRPC](https://juejin.im/post/5cff855c518825612f412526)|翻译|3.5| @@ -6846,10 +6848,11 @@ |[The JavaScript Tutorial 翻译](https://github.com/xitu/javascript-tutorial-en)|翻译校对|3| |[The JavaScript Tutorial 翻译](https://github.com/xitu/javascript-tutorial-en)|翻译校对|1| -## 译者:[Moonliujk](https://github.com/Moonliujk) 历史贡献积分:81 当前积分:26 二零一九:16 +## 译者:[Moonliujk](https://github.com/Moonliujk) 历史贡献积分:83 当前积分:28 二零一九:18 |文章|类型|积分| |------|-------|-------| +|[Git:透过命令学概念 —— 第二部分](https://juejin.im/post/5d2da05ae51d45106b15ffca)|校对|2| |[使用 SVG 和 Vue.Js 构建动态树图](https://juejin.im/post/5d2806fb518825121c0058d8)|校对|4| |[从原型图到成品:步步深入 CSS 布局](https://juejin.im/post/5cebb52651882530be7b16a4)|校对|2| |[使用 closest() 函数获取正确的 DOM 元素](https://juejin.im/post/5cc811796fb9a0321c45e5e0)|校对|0.5| @@ -7148,10 +7151,11 @@ |[TensorFlow 官方文档翻译](https://github.com/xitu/tensorflow-docs)|翻译校对|8| |[用 Scikit-Learn 实现 SVM 和 Kernel SVM](https://juejin.im/post/5b7fd39af265da43831fa136)|校对|2| -## 译者:[TrWestdoor](https://github.com/TrWestdoor) 历史贡献积分:37.5 当前积分:27.5 二零一九:14.5 +## 译者:[TrWestdoor](https://github.com/TrWestdoor) 历史贡献积分:39 当前积分:29 二零一九:16 |文章|类型|积分| |------|-------|-------| +|[在 Python 中过度使用列表解析器和生成表达式](https://juejin.im/post/5d281b0ff265da1b8b2b8ae0)|校对|1.5| |[在机器学习中为什么要进行 One-Hot 编码?](https://juejin.im/post/5d15840e5188255c23553204)|校对|1| |[使用 What-If 工具来研究机器学习模型](https://juejin.im/post/5d143abff265da1bb80c4005)|校对|2| |[时间序列分析、可视化、和使用 LSTM 预测](https://juejin.im/post/5cecdbb75188252db706f4e9)|校对|1.5| @@ -7524,10 +7528,11 @@ |------|-------|-------| |[深入理解 React 高阶组件](https://juejin.im/entry/5bdd226cf265da616f6f6cce)|校对|4| -## 译者:[iWeslie](https://github.com/iWeslie) 历史贡献积分:132 当前积分:80 二零一九:88 +## 译者:[iWeslie](https://github.com/iWeslie) 历史贡献积分:132 当前积分:81 二零一九:89 |文章|类型|积分| |------|-------|-------| +|[Xcode 和 LLDB 高级调试教程:第 2 部分](https://juejin.im/post/5d2321eee51d454f71439d64)|校对|1| |2019 年 7 月兑小米台灯 1 个,掘金鼠标垫 1 个,掘金纪念币 2 个|减去积分|52| |[Xcode 和 LLDB 高级调试教程:第 1 部分](https://juejin.im/post/5d0b246be51d4555e372a60b)|校对|2| |[用于 iOS 的 ML Kit 教程:识别图像中的文字](https://juejin.im/post/5cfe23af6fb9a07ee742d401)|校对|2| @@ -8018,10 +8023,12 @@ |[💅 styled-components 背后的魔力](https://juejin.im/post/5c6d3a32e51d451804732248)|翻译|3| |推荐英文文章一篇|奖励|1| -## 译者:[shixi-li](https://github.com/shixi-li) 历史贡献积分:59 当前积分:59 二零一九:59 +## 译者:[shixi-li](https://github.com/shixi-li) 历史贡献积分:64.5 当前积分:64.5 二零一九:64.5 |文章|类型|积分| |------|-------|-------| +|[思考实践:用 Go 实现 Flutter](https://juejin.im/post/5d215b8df265da1b7b31ac8f)|翻译|3.5| +|[Git:透过命令学概念 —— 第二部分](https://juejin.im/post/5d2da05ae51d45106b15ffca)|校对|2| |[使用 SVG 和 Vue.Js 构建动态树图](https://juejin.im/post/5d2806fb518825121c0058d8)|校对|1.5| |[TypeScript 3.0: unknown 类型](https://juejin.im/post/5d04ac745188250a8b1fd203)|翻译|4| |[线性代数:矩阵基本运算](https://juejin.im/post/5d107b00f265da1b67211a21)|校对|1| @@ -8069,10 +8076,11 @@ |[为什么你的应用需要对各种尺寸屏幕做适配优化?](https://juejin.im/post/5c662023518825767633ab86)|校对|2.5| |[提取图像中的文字、人脸或者条形码 — 形状检测 API](https://juejin.im/post/5c64026fe51d457f963d249c)|翻译|4| -## 译者:[Mirosalva](https://github.com/Mirosalva) 历史贡献积分:63.5 当前积分:63.5 二零一九:63.5 +## 译者:[Mirosalva](https://github.com/Mirosalva) 历史贡献积分:71 当前积分:71 二零一九:71 |文章|类型|积分| |------|-------|-------| +|[Git:透过命令学概念 —— 第二部分](https://juejin.im/post/5d2da05ae51d45106b15ffca)|翻译|7.5| |[在 npm 上启用现在 JavaScript](https://juejin.im/post/5d15d64be51d4510a5033603)|翻译|6.5| |[Commit 提交指南](https://juejin.im/post/5ccf9e60f265da039c05659d)|翻译|4| |[Node.js 提供百万的活跃 WebSocket 连接](https://juejin.im/post/5cbeb2f45188250ab65f1d0c)|翻译|3| @@ -8153,10 +8161,11 @@ |[Widget - State - Context - InheritedWidget](https://juejin.im/post/5c768ad2f265da2dce1f535c)|校对|3| |[Swift:通过示例避免内存泄漏](https://juejin.im/post/5c6a0abaf265da2dc675a9b2)|校对|1| -## 译者:[kirinzer](https://github.com/kirinzer) 历史贡献积分:13 当前积分:13 二零一九:13 +## 译者:[kirinzer](https://github.com/kirinzer) 历史贡献积分:17 当前积分:17 二零一九:17 |文章|类型|积分| |------|-------|-------| +|[Xcode 和 LLDB 高级调试教程:第 2 部分](https://juejin.im/post/5d2321eee51d454f71439d64)|翻译|4| |[Xcode 和 LLDB 高级调试教程:第 1 部分](https://juejin.im/post/5d0b246be51d4555e372a60b)|翻译|4| |[懒加载变量在 iOS Swift](https://juejin.im/post/5ca775b26fb9a05e3527db37)|翻译|2.5| |[在 iOS 上使用 Carthage 建立依赖](https://juejin.im/post/5c4f04ef51882525eb3663be)|校对|1.5| @@ -8334,10 +8343,11 @@ |[谷歌搜索操作符大全(包含 42 个高级操作符)](https://juejin.im/post/5c73744ef265da2dc675c029)|校对|2.5| |[用 Rust 写一个微服务](https://juejin.im/post/5c7a3777f265da2dd773fc38)|翻译|8| -## 译者:[LucaslEliane](https://github.com/LucaslEliane) 历史贡献积分:26 当前积分:26 二零一九:26 +## 译者:[LucaslEliane](https://github.com/LucaslEliane) 历史贡献积分:34 当前积分:34 二零一九:34 |文章|类型|积分| |------|-------|-------| +|[使用 Swift 5 构建一个 iOS 移动端群聊应用程序](https://juejin.im/post/5d2c6e846fb9a07ebb0564ae)|翻译|8| |[高效地在 Flutter 中使用 BLoC 模式](https://juejin.im/post/5cf7abf65188256bdd2ee76b)|翻译|4| |[使用 closest() 函数获取正确的 DOM 元素](https://juejin.im/post/5cc811796fb9a0321c45e5e0)|翻译|2| |[使用网格布局实现响应式图片](https://juejin.im/post/5ca0ad80f265da30920059ae)|翻译|4.5| @@ -8384,10 +8394,11 @@ |[四个理由让你使用灰度色调进行设计](https://juejin.im/post/5c961b1fe51d456a6743109e)|校对|1.5| |[浏览器中 CSS 支持指南](https://juejin.im/post/5c87a9006fb9a049e4138c7e)|校对|2.5| -## 译者:[Endone](https://github.com/Endone) 历史贡献积分:21 当前积分:21 二零一九:21 +## 译者:[Endone](https://github.com/Endone) 历史贡献积分:24 当前积分:24 二零一九:24 |文章|类型|积分| |------|-------|-------| +|[使用 Swift 5 构建一个 iOS 移动端群聊应用程序](https://juejin.im/post/5d2c6e846fb9a07ebb0564ae)|校对|3| |[剖析 Stack Overflow,开发者遇到最多的的 Bug 是哪些?](https://juejin.im/post/5d087a32518825403d14758b)|校对|1.5| |[JavaScript 线性代数:向量](https://juejin.im/post/5cf61bf8e51d45775653674e)|校对|1.5| |[自动补全规则](https://juejin.im/post/5cd556ef6fb9a03218556cb7)|校对|1| @@ -8550,10 +8561,11 @@ |[用 Rust 打造你的第一个命令行工具](https://juejin.im/post/5cb33b94e51d456e63760453)|校对|1.5| |[如何在远程服务器上运行 Jupyter Notebooks](https://juejin.im/post/5cb5e0a9f265da036c577f24)|校对|1.5| -## 译者:[ccJia](https://github.com/ccJia) 历史贡献积分:32 当前积分:32 二零一九:32 +## 译者:[ccJia](https://github.com/ccJia) 历史贡献积分:37 当前积分:37 二零一九:37 |文章|类型|积分| |------|-------|-------| +|[在 Python 中过度使用列表解析器和生成表达式](https://juejin.im/post/5d281b0ff265da1b8b2b8ae0)|翻译|5| |[在 Keras 下使用自编码器分类极端稀有事件](https://juejin.im/post/5cff17296fb9a07ec63b0a7f)|翻译|5| |[在数据可视化中,我们曾经“画”下的那些错误](https://juejin.im/post/5cd39e1de51d453a3a0acb7b)|翻译|7| |[归一化和标准化 — 量化分析](https://juejin.im/post/5cc5c0a06fb9a0321b69740a)|翻译|7| @@ -8604,10 +8616,11 @@ |------|-------|-------| |[创意运用 Console API!](https://juejin.im/post/5cc1517e5188252e7a0247dd)|校对|2| -## 译者:[Charlo-O](https://github.com/Charlo-O) 历史贡献积分:25 当前积分:25 二零一九:25 +## 译者:[Charlo-O](https://github.com/Charlo-O) 历史贡献积分:31 当前积分:31 二零一九:31 |文章|类型|积分| |------|-------|-------| +|[贫困线下的软件 — 开源项目的可持续发展问题探讨](https://juejin.im/post/5d215da2e51d4556be5b3acb)|翻译|6| |[感受 4px 基线网格带来的便利](https://juejin.im/post/5d09e5ecf265da1b60290798)|校对|1| |[微设计系统 — 打破藩篱](https://juejin.im/post/5d0335395188255ee806a5da)|翻译|7| |[关于 Flutter 页面路由过渡动画,你所需要知道的一切](https://juejin.im/post/5ceb6179f265da1bc23f55d0)|校对|0.5| @@ -8684,10 +8697,11 @@ |[自动补全规则](https://juejin.im/post/5cd556ef6fb9a03218556cb7)|翻译|3| |[使用 PyTorch 在 MNIST 数据集上进行逻辑回归](https://juejin.im/post/5cc66d946fb9a032286173a7)|校对|1| -## 译者:[Chorer](https://github.com/Chorer) 历史贡献积分:10.5 当前积分:10.5 二零一九:10.5 +## 译者:[Chorer](https://github.com/Chorer) 历史贡献积分:14 当前积分:14 二零一九:14 |文章|类型|积分| |------|-------|-------| +|[贫困线下的软件 — 开源项目的可持续发展问题探讨](https://juejin.im/post/5d215da2e51d4556be5b3acb)|校对|3.5| |[CSS 思维模式](https://juejin.im/post/5d295380f265da1bab29dc13)|校对|1.5| |推荐英文文章一篇|奖励|1| |[Commit 提交指南](https://juejin.im/post/5ccf9e60f265da039c05659d)|校对|2.5| @@ -8755,10 +8769,11 @@ |[声音设计与无声设计](https://juejin.im/post/5ce354bee51d4510727c7fd1)|翻译|2+0.5| |[Swift 里的强制 @inline 注解](https://juejin.im/post/5cd67d64518825686244635a)|校对|1.5| -## 译者:[suhanyujie](https://github.com/suhanyujie) 历史贡献积分:45 当前积分:45 二零一九:45 +## 译者:[suhanyujie](https://github.com/suhanyujie) 历史贡献积分:56 当前积分:56 二零一九:56 |文章|类型|积分| |------|-------|-------| +|[思考实践:用 Go 实现 Flutter](https://juejin.im/post/5d215b8df265da1b7b31ac8f)|翻译|11| |[使用 Gomobile 和 Gopherjs 的动态二维码数据传输](https://juejin.im/post/5d2bfccef265da1bb77699e8)|校对|3.5| |[通过 Rust 学习解析器组合器 — 第三部分](https://juejin.im/post/5d1f29f7f265da1b961324b2)|翻译|5.5| |推荐英文文章一篇|奖励|1| @@ -8894,10 +8909,13 @@ |[改善 Android Studio 的构建速度](https://juejin.im/post/5d1388b1f265da1b6d403560)|校对|1.5| |[揭秘变量提升](https://juejin.im/post/5d026b71518825710d2b1f63)|校对|1.5| -## 译者:[JalanJiang](https://github.com/JalanJiang) 历史贡献积分:26 当前积分:26 二零一九:26 +## 译者:[JalanJiang](https://github.com/JalanJiang) 历史贡献积分:42 当前积分:42 二零一九:42 |文章|类型|积分| |------|-------|-------| +|[利用 84 种认知偏见设计更好的产品 —— 第一部分](https://juejin.im/post/5d2acf995188254c1915bd12)|翻译|6| +|[在 Python 中过度使用列表解析器和生成表达式](https://juejin.im/post/5d281b0ff265da1b8b2b8ae0)|校对|4| +|[使用 Swift 5 构建一个 iOS 移动端群聊应用程序](https://juejin.im/post/5d2c6e846fb9a07ebb0564ae)|校对|6| |[Kubernetes 儿童插图指南](https://juejin.im/post/5d1b2a656fb9a07edc0b7058)|翻译|3| |[The JavaScript Tutorial 教程](https://github.com/javascript-tutorial/zh.javascript.info)|翻译校对|4| |推荐英文文章一篇|奖励|1| @@ -8939,19 +8957,22 @@ |[Web 流式文字排版的现状](https://juejin.im/post/5d267d9de51d45773d4686ab)|翻译|7| |[微前端:未来前端开发的新趋势 — 第一部分](https://juejin.im/post/5d0e367b6fb9a07ebf4b781a)|翻译|4| -## 译者:[MarchYuanx](https://github.com/MarchYuanx) 历史贡献积分:9 当前积分:9 二零一九:9 +## 译者:[MarchYuanx](https://github.com/MarchYuanx) 历史贡献积分:12 当前积分:12 二零一九:12 |文章|类型|积分| |------|-------|-------| +|[设计任何图表的六项原则](https://juejin.im/post/5d27fca7f265da1b5e731f92)|翻译|3| |[CSS 思维模式](https://juejin.im/post/5d295380f265da1bab29dc13)|翻译|3.5| |[自托管你的静态资源](https://juejin.im/post/5d258a77f265da1bca5202dc)|校对|2| |[设计师如何成长为 Leader?](https://juejin.im/post/5d172fca6fb9a07eda032c6f)|校对|2| |[在 npm 上启用现在 JavaScript](https://juejin.im/post/5d15d64be51d4510a5033603)|校对|1.5| -## 译者:[mymmon](https://github.com/mymmon) 历史贡献积分:7 当前积分:7 二零一九:7 +## 译者:[mymmon](https://github.com/mymmon) 历史贡献积分:10.5 当前积分:10.5 二零一九:10.5 |文章|类型|积分| |------|-------|-------| +|[设计任何图表的六项原则](https://juejin.im/post/5d27fca7f265da1b5e731f92)|校对|2| +|[利用 84 种认知偏见设计更好的产品 —— 第一部分](https://juejin.im/post/5d2acf995188254c1915bd12)|校对|1.5| |[Kubernetes 儿童插图指南](https://juejin.im/post/5d1b2a656fb9a07edc0b7058)|校对|1| |[Web 流式文字排版的现状](https://juejin.im/post/5d267d9de51d45773d4686ab)|校对|4| |[npm 的经济风云 —— 上半部分](https://juejin.im/post/5d146225e51d4556db694a4b)|校对|2| @@ -8968,10 +8989,11 @@ |------|-------|-------| |[The JavaScript Tutorial 教程](https://github.com/javascript-tutorial/zh.javascript.info)|翻译校对|3| -## 译者:[solerji](https://github.com/solerji) 历史贡献积分:4 当前积分:4 二零一九:4 +## 译者:[solerji](https://github.com/solerji) 历史贡献积分:6 当前积分:6 二零一九:6 |文章|类型|积分| |------|-------|-------| +|[区域设置更改和 AndroidViewModel 反面模式](https://juejin.im/post/5d2db596f265da1b5f2688d3)|翻译|2| |[CSS 思维模式](https://juejin.im/post/5d295380f265da1bab29dc13)|校对|0.5| |[微前端:未来前端开发的新趋势 — 第四部分](https://juejin.im/post/5d23394ae51d45778f076db0)|校对|1.5| |[The JavaScript Tutorial 教程](https://github.com/javascript-tutorial/zh.javascript.info)|翻译校对|2| @@ -9005,3 +9027,16 @@ |文章|类型|积分| |------|-------|-------| |[The JavaScript Tutorial 教程](https://github.com/javascript-tutorial/zh.javascript.info)|翻译校对|5| + +## 译者:[JasonWu1111](https://github.com/JasonWu1111) 历史贡献积分:1.5 当前积分:1.5 二零一九:1.5 + +|文章|类型|积分| +|------|-------|-------| +|[Xcode 和 LLDB 高级调试教程:第 2 部分](https://juejin.im/post/5d2321eee51d454f71439d64)|校对|1.5| + +## 译者:[shinichi4849](https://github.com/shinichi4849) 历史贡献积分:3.5 当前积分:3.5 二零一九:3.5 + +|文章|类型|积分| +|------|-------|-------| +|[设计任何图表的六项原则](https://juejin.im/post/5d27fca7f265da1b5e731f92)|校对|1| +|[利用 84 种认知偏见设计更好的产品 —— 第一部分](https://juejin.im/post/5d2acf995188254c1915bd12)|校对|2.5| From 4aa4854d5fe19bc447466b8424efe6f598155278 Mon Sep 17 00:00:00 2001 From: LeviDing Date: Tue, 16 Jul 2019 22:16:32 +0800 Subject: [PATCH 63/68] =?UTF-8?q?fix=20=E7=A7=AF=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- integrals.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integrals.md b/integrals.md index d6b5bd586b9..913d11485ee 100644 --- a/integrals.md +++ b/integrals.md @@ -7528,7 +7528,7 @@ |------|-------|-------| |[深入理解 React 高阶组件](https://juejin.im/entry/5bdd226cf265da616f6f6cce)|校对|4| -## 译者:[iWeslie](https://github.com/iWeslie) 历史贡献积分:132 当前积分:81 二零一九:89 +## 译者:[iWeslie](https://github.com/iWeslie) 历史贡献积分:133 当前积分:81 二零一九:89 |文章|类型|积分| |------|-------|-------| From fe49e75059e856f08dd8bcbe64408102bf7acc2e Mon Sep 17 00:00:00 2001 From: Lucas biu <517197934@qq.com> Date: Thu, 1 Aug 2019 13:02:42 +0800 Subject: [PATCH 64/68] =?UTF-8?q?NodeJS=20=E8=AF=BB=E5=8F=96=E5=A4=A7?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E9=9B=86=E5=90=88=E7=9A=84=E5=87=A0=E7=A7=8D?= =?UTF-8?q?=E6=96=B9=E6=B3=95=E7=9A=84=E6=80=A7=E8=83=BD=E6=AF=94=E8=BE=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...methods-for-reading-large-datasets-pt-2.md | 176 +++++++++--------- 1 file changed, 88 insertions(+), 88 deletions(-) diff --git a/TODO1/streams-for-the-win-a-performance-comparison-of-nodejs-methods-for-reading-large-datasets-pt-2.md b/TODO1/streams-for-the-win-a-performance-comparison-of-nodejs-methods-for-reading-large-datasets-pt-2.md index 0e8c55bc8a4..5001a165db2 100644 --- a/TODO1/streams-for-the-win-a-performance-comparison-of-nodejs-methods-for-reading-large-datasets-pt-2.md +++ b/TODO1/streams-for-the-win-a-performance-comparison-of-nodejs-methods-for-reading-large-datasets-pt-2.md @@ -2,209 +2,209 @@ > * 原文作者:[Paige Niedringhaus](https://medium.com/@paigen11) > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/streams-for-the-win-a-performance-comparison-of-nodejs-methods-for-reading-large-datasets-pt-2.md](https://github.com/xitu/gold-miner/blob/master/TODO1/streams-for-the-win-a-performance-comparison-of-nodejs-methods-for-reading-large-datasets-pt-2.md) -> * 译者: +> * 译者:[lucasleliane](https://github.com/LucaslEliane) > * 校对者: -# Streams For the Win: A Performance Comparison of NodeJS Methods for Reading Large Datasets (Pt 2) +# 胜者是 Stream:NodeJS 读取大数据集合几种方法的性能比较 -## How readFile(), createReadStream() and event-stream Stack Up Against One Another +## readFile()、createReadStream() 以及事件流如何相互比较 ![](https://cdn-images-1.medium.com/max/2000/1*fsseXIPGEhwmg6kfgXyIjA.jpeg) -If you’ve been keeping up with my writing, a few weeks ago, I published a [blog](https://itnext.io/using-node-js-to-read-really-really-large-files-pt-1-d2057fe76b33) talking about a variety of ways to use Node.js to read really large datasets. +如果你最近都在阅读我的文章,你应该会看到我几周前发布的一篇[博客](https://itnext.io/using-node-js-to-read-really-really-large-files-pt-1-d2057fe76b33),这篇博客讨论了使用 Node.js 来读取大型数据集的各种方法。 -To my surprise, it did exceptionally well with readers — this seemed (to me) like a topic many others have already covered in posts, blogs and forums, but for whatever reason, it got the attention of a lot of people. So, thank you to all of you who took the time to read it! I really appreciate it. +令我惊讶的是,这篇博客受到了很多读者的喜爱 - 这个主题(对于我来说)似乎在很多其他的帖子、博客或者论坛上已经讨论过了,但是无论如何,它都吸引了很多人的关注。所以,感谢所有花时间来阅读这篇博客的读者!对此,我真的非常感激。 -One particularly astute reader ([Martin Kock](undefined)), went so far as to ask how long it took to parse the files. It seemed as if he’d read my mind, because part two of my series on using Node.js to read really, really large files and datasets involves just that. +一位特别敏锐的读者([Martin Kock](undefined))甚至问到了解析文件到底需要多长时间。看起来他已经读懂了我的想法,因为这个系列文章的第二部分就和这个问题有关。 -> Here, I’ll evaluate the three different methods in Node.js I used to read the files, to determine which is most performant. +> 在这里,我将评估 Node.js 读取文件的三种不同方法,来确定哪一种方法性能最佳。 -#### The Challenge From Part 1 +### 上一篇文章中的挑战 -I won’t go into the specifics of the challenge and solution, because you can read my first post for all the details [here](https://itnext.io/using-node-js-to-read-really-really-large-files-pt-1-d2057fe76b33), but I will give you the high level overview. +我不会详细介绍上一篇博客中的挑战和解决方案,因为你可以去阅读我的第一篇文章,了解所有的细节[这里](https://itnext.io/using-node-js-to-read-really-really-large-files-pt-1-d2057fe76b33),但是我会给你进行一下简单的介绍。 -A person from a Slack channel I’m a member of, posted a coding challenge he’d received, which involved reading in a very large dataset (over 2.5GB in total), parsing through the data and pulling out various pieces of information. +这个挑战是来自于 Slack 频道的人发布的一个编码挑战,要求读取一个非常大的数据集(总共大小超过 2.5 GB),解析数据并且提取各种信息。 -It challenged programmers to print: +这个挑战让程序员们打印出: -* Total lines in the file, -* Names in the 432nd and 43243rd indexes, -* Counts for total numbers donations per month, -* And the most common first name in the files and how a count of how often it occurred. +* 文件总行数, +* 第 432 和第 43243 行的中的名字, +* 每月捐款总数, +* 以及文件中最常见的名字和它出现的频率的计数。 -Link to the data: ​[https://www.fec.gov/files/bulk-downloads/2018/indiv18.zip](https://www.fec.gov/files/bulk-downloads/2018/indiv18.zip) +这里是数据链接:​[https://www.fec.gov/files/bulk-downloads/2018/indiv18.zip](https://www.fec.gov/files/bulk-downloads/2018/indiv18.zip) -#### The Three Different Solutions Possible For Smaller Datasets +#### 三种解决方案与小数据集情况下的不同点 -As I worked towards my ultimate end goal of processing a large dataset, I came up with three solutions in Node.js. +当我努力去实现处理大型数据集这个目标的过程中,我在 Node.js 中提出了三个解决方案。 -**Solution #1: [`fs.readFile()`](https://nodejs.org/api/fs.html#fs_fs_readfile_path_options_callback)** +**解决方案 1:[`fs.readFile()`](https://nodejs.org/api/fs.html#fs_fs_readfile_path_options_callback)** -The first involved Node.js’s native method of `fs.readFile()`, and consisted of reading in the whole file, holding it in memory and performing the operations on the entire file, then returning the results. At least for smaller files, it worked, but when I got to the largest file size, my server crashed with a JavaScript `heap out of memory` error. +第一个解决方案涉及到 Node.js 的 `fs.readFile()` 原生方法,包括读取整个文件,将其保存在内存中并且对整个文件执行操作,然后返回结果。至少对于较小的文件,这个方法是没问题的,但是当我使用最大的文件的时候,我的服务器崩溃了,并且抛出了一个 `heap out of memory` 错误。 -**Solution #2: [fs.createReadStream()](https://nodejs.org/api/fs.html#fs_fs_createreadstream_path_options) & [rl.readLine()](https://nodejs.org/api/readline.html#readline_event_line)** +**解决方案 2:[`fs.createReadStream()`](https://nodejs.org/api/fs.html#fs_fs_createreadstream_path_options) 以及 [`rl.readLine()`](https://nodejs.org/api/readline.html#readline_event_line)** -My second solution also involved another couple of methods native to Node.js: `fs.createReadStream()` and `rl.readLine()`. In this iteration, the file was streamed through Node.js in an `input` stream, and I was able to perform individual operations on each line, then cobble all those results together in the `output` stream. Again, this worked pretty well on smaller files, but once I got to the biggest file, the same error happened. Although Node.js was streaming the inputs and outputs, it still attempted to hold the whole file in memory while performing the operations (and couldn’t handle the whole file). +我的第二个解决方案还涉及了 Node.js 的另外几个方法:`fs.createReadStream()` 和 `rl.readLine()`。在这个方案中,文件可以通过 Node.js 的 `input` 流,进行流式传输,我们能够对每一行进行单独操作,然后在 `output` 流中将所有的结果拼在一起。同样,这种方案在较小的文件上能够很好地完成工作,但是一旦输入最大的文件,就会发生和方案 1 同样的错误。虽然 Node.js 正在对输入和输出进行流式传输,但是 Node.js 在执行操作的时候,仍然会试图将整个文件保存在内存中(并且无法一次处理整个文件)。 -**Solution #3: [`event-stream`](https://www.npmjs.com/package/event-stream)** +**解决方案 3:[`event-stream`](https://www.npmjs.com/package/event-stream)** -In the end, I came up with only one solution in Node.js that was able to handle the full 2.55GB file I wanted to parse through, at one time. +最后,我在 Node.js 中提出了唯一能够处理完整的 2.55 GB 的文件的解决方案。 -> Fun fact: Node.js can only hold up to 1.67GB in memory at any one time, after that, it throws JavaScript `heap out of memory` error. +> 有趣的是:Node.js 在任何时候,都只能够在内存中容纳 1.67 GB,之后就会抛出 JavaScript 的 `heap out of memory` 错误。 -My solution involved a popular NPM package called [event-stream](https://www.npmjs.com/package/event-stream), which actually let me perform operations on the **throughput stream** of data, instead of just the input and output streams, as Node.js’s native capabilities allow. +我的解决方案涉及到一个流行的名为 [event-stream](https://www.npmjs.com/package/event-stream) 的 NPM 包,这个包允许我对数据的整个**吞吐流**执行操作,而不仅仅是原生 Node.js 提供的输入和输出流操作。 -You can see all three of my solutions [here](https://github.com/paigen11/file-read-challenge) in Github. +你可以在 Github 中的[这里](https://github.com/paigen11/file-read-challenge)找到我的三个解决方案。 -And I solved the problem, which was my initial goal, but it got me thinking: was my solution really the most performant of the three options? +我完成了我最初的目标,解决了这个问题,但是这个问题还是让我陷入了思考:我的解决方案真的是三个方案中最高效的吗? -#### Comparing Them To Find The Optimal Solution +### 比较,并且找到最优的解决方案 -Now, I had a new goal: determine which of my solutions was best. +现在,我有了一个新的目标:确定哪种解决方案是最好的。 -Since I couldn’t use the full 2.55GB file with the Node.js native solutions, I chose to use one of the smaller files that was about 400MB worth of data, that I’d used for testing while I was developing my solutions. +由于我没有办法使用原生 Node.js 的方案来处理完整的 2.55 GB 大小的文件,因此我选择使用一个较小的文件,这些文件大约有 400 MB 的数据,我在实现解决方案的时候,使用这个数据集来进行测试。 -For performance testing Node.js, I came across two ways to keep track of the file and individual function processing times, and I decided to incorporate both to see how great the differences were between the two methods (and make sure I wasn’t completely off the rails with my timing). +对于 Node.js 性能测试,我发现了两种跟踪文件和函数处理时间的方法,我决定将两者结合起来看看这两种方法之间的差异有多大(并且确保我测试出来的时间不会完全偏离事实)。 -**[`console.time()`](https://nodejs.org/api/console.html#console_console_time_label) & [`console.timeEnd()`](https://nodejs.org/api/console.html#console_console_timeend_label)** +**[`console.time()`](https://nodejs.org/api/console.html#console_console_time_label) 和 [`console.timeEnd()`](https://nodejs.org/api/console.html#console_console_timeend_label)** -Node.js has some handy, built-in methods available to it for timing and performance testing, called `console.time()` and `console.timeEnd()`, respectively. To use these methods, I only had to pass in the same label parameter for both `time()` and `timeEnd()`, like so, and Node’s smart enough to output the time between them after the function’s done. +Node.js 有一些方便的内置方法,可以用于定时和性能测试,分别是 `console.time()` 和 `console.timeEnd()`。要使用这些方法,我只需要为 `time()` 和 `timeEnd()` 传递相同的 label 参数,就像下面这样,Node 就会在函数执行完成之后,输出两者之间的时间差。 ``` -// timer start +// 定时器启动 console.time('label1'); -// run function doing something in the code +// 执行自定义函数 doSomething(); -// timer end, where the difference between the timer start and timer end is printed out +// 定时器结束,会打印出来定时器启动和结束之间的时间差 console.timeEnd('label1'); -// output in console looks like: label1 0.002ms +// 输出的结果类似于这样: label1 0.002ms ``` -That’s one method I used to figure out how long it took to process the dataset. +这是我用来计算处理数据集所需要的时间的一种方法。 [**`performance-now`**](https://www.npmjs.com/package/performance-now) -The other, tried and well-liked performance testing module I came across for Node.js is hosted on NPM as [`performance-now`](https://www.npmjs.com/package/performance-now). +另外,我还发现了一个久经考验并且广受欢迎的 Node.js 性能测试模块,这个模块是 [`performance-now`](https://www.npmjs.com/package/performance-now),它被托管在 NPM 上面。 -7+ million downloads per week from NPM, can’t be too wrong, right?? +这个模块在 NPM 上面每周都有着 700 多万的下载量,不会错的,对吧? -Implementing the `performance-now` module into my files was also almost as easy as the native Node.js methods, too. Import the module, set a variable for the start and end of the instantiation of the method, and compute the time difference between the two. +将 `performance-now` 模块引入到我的文件中,几乎和原生 Node.js 方法一样简单。导入模块,设置方法执行开始和执行结束的结果设置变量,并且计算两者之间的时间差。 ``` -// import the performance-now module at the top of the file +// 在文件开头导入 performance-now 模块 const now = require('performance-now'); -// set the start of the timer as a variable +// 为定时器的起始状态设置变量 const start = now(); -// run function doing something in the code +// 执行自定义函数 doSomething(); -// set the end of the timer as a variable +// 为定时器的结束状态设置变量 const end = now(); -// Compute the duration between the start and end +// 计算定时器起始和结束的时间差 console.log('Performance for timing for label:' + (end — start).toFixed(3) + 'ms'; -// console output looks like: Performance for timing label: 0.002ms +// 打印出的结果类似于这样: Performance for timing label: 0.002ms ``` -I figured that by using both Node’s `console.time()` and `performance-now` at the same time, I could split the difference and get a pretty accurate read on how long my file parsing functions were really taking. +我想同时使用 Node 的 `console.time()` 和 `performance-now`,我可以规避差异并且获得关于文件解析函数真正的执行时间的准确值。 -Below are code snippets implementing `console.time()` and `performance-now` in each of my scripts. These are only snippets of one function each — for the full code, you can see my repo [here](https://github.com/paigen11/file-read-challenge). +下面是我在每个脚本中接入 `console.time()` 以及 `performance-now` 的代码片段。这些代码只是每个函数的片段 - 相对于完整代码来说,你可以在[这里](https://github.com/paigen11/file-read-challenge)查看我的代码仓库。 -**Fs.readFile() Code Implementation Sample** +**fs.readFile() 代码实现示例** ![](https://cdn-images-1.medium.com/max/2568/1*n48UZ77lvktwjN6IDR0x1g.png) -Since this script is using the `fs.readFile()` implementation, where the entire file is read into memory before any functions are executed on it, this is the most synchronous-looking code. It’s not actually synchronous, that’s an entirely separate Node method called `fs.readFileSync()`, it just resembles it . +由于这个脚本使用 `fs.readFile()` 实现,整个文件都会在执行函数之前被读取到内存中,看起来这是最同步的代码。但是它实际上不是同步的,同步地读取文件在 Node 中有一个专用的方法,叫做 `fs.readFileSync()`,两者看起来很相似。 -But it’s easy to see the total line count of the file and the two timing methods bookending it to determine how long it takes to execute the line count. +但是,我们很容易看到文件的总行数以及两个计时器方法,来确定执行行计数到底花费了多长时间。 -**Fs.createReadStream() Code Implementation Sample** +**fs.createReadStream() 代码实现示例** -**Input Stream (line-by-line):** +**输入流(按行读取):** ![](https://cdn-images-1.medium.com/max/2568/1*XwIXtNCMSmCJBu7DX4zxGA.png) -**Output Stream (once full file’s been read during input):** +**输出流(输入时一次性读取完整文件):** ![](https://cdn-images-1.medium.com/max/2568/1*rhhHpFIS5b-UdluXYgLaIg.png) -As the second solution using `fs.createReadStream()` involved creating an input and output stream for the file, I broke the code snippets into two separate screenshots, as the first is from the input stream (which is running through the code line by line) and the second’s the output stream (compiling all the resulting data). +由于第二个解决方案使用了 `fs.createReadStream()`,其涉及到了为文件创建输入输出流,所以我将代码片段分成了两个独立的截图,第一个是输入流(逐行进行文件读取)以及第二个输出流(计算所有的结果数据)。 -**Event Stream Code Implementation Sample** +**事件流代码示例** -**Through Stream (also line-by-line):** +**输入流(同样是逐行)** ![](https://cdn-images-1.medium.com/max/2568/1*UzzXjaStCYMgUHHE_qBiqw.png) -**On Stream End:** +**流结束:** ![](https://cdn-images-1.medium.com/max/2568/1*rgZQKTXROxXn6T9Gmqc0oA.png) -The `event-stream` solution looks pretty similar to the `fs.createReadStream()`, except instead of an **input stream**, the data is processed in a **throughput stream**. And then once the whole file’s been read and all the functions have been done on the file, the stream’s ended and the required information is printed out. +`event-stream` 的解决方案看起来和 `fs.createReadStream()` 非常相似,除了**输入流**,在这个解决方案中,数据通过**吞吐流**来进行处理。然后,一旦整个文件被读取并且所有计算都已经完成,则表示流程结束,并且打印出所需要的信息。 -#### Results +### 结果 -Now on to the moment we’ve all been waiting for: the results! +现在,来看看我们一直期待的:结果。 -I ran all three of my solutions against the same 400MB dataset, which contained almost 2 million records to parse through. +我针对相同的 400 MB 大小的数据集运行了全部三种解决方案,其中包含了需要解析的将近 200 万条记录。 ![Streams for the win!](https://cdn-images-1.medium.com/max/4056/1*K3fMjpvkyTMccexwsa3gjw.png) -As you can see from the table, `fs.createReadStream()` and `event-stream` both fared well, but overall, `event-stream` has to be the grand winner in my mind, if only for the fact that it can process much larger file sizes than either `fs.readFile()` or `fs.createReadStream()`. +从表中可以看出,`fs.createReadStream()` 和 `event-stream` 都表现很好,但是总的来说,`event-stream` 是我心目中的大赢家,因为相比起 `fs.readFile()` 或者 `fs.createReadStream()` 来说,它可以处理的文件大小要大得多。 -The percentage improvements are included at the end of the table above as well, for reference. +提升的百分比也在表格最后展示出来了。 -`fs.readFile()` just got blown out of the water by the competition. By streaming the data, processing times for the file improved by at least 78% — sometimes close to almost a 100%, which is pretty darn, impressive. +`fs.readFile()` 被竞争对手们完全击败了。通过流来传输数据,文件的处理时间提高了至少 78% - 有时接近 100%,这让人印象非常深刻。 -Below are the raw screenshots from my terminal for each of my solutions. +以下是执行每个解决方案的终端截图。 -**Solution #1: [`fs.readFile()`](https://nodejs.org/api/fs.html#fs_fs_readfile_path_options_callback)** +**解决方案 1: [`fs.readFile()`](https://nodejs.org/api/fs.html#fs_fs_readfile_path_options_callback)** -![The solution using only: fs.readFile()](https://cdn-images-1.medium.com/max/2000/1*luMWmrPikShHXtu6yScO9g.png) +![仅使用 fs.readFile()](https://cdn-images-1.medium.com/max/2000/1*luMWmrPikShHXtu6yScO9g.png) -**Solution #2: [fs.createReadStream()](https://nodejs.org/api/fs.html#fs_fs_createreadstream_path_options) & [rl.readLine()](https://nodejs.org/api/readline.html#readline_event_line)** +**解决方案 2: [fs.createReadStream()](https://nodejs.org/api/fs.html#fs_fs_createreadstream_path_options) & [rl.readLine()](https://nodejs.org/api/readline.html#readline_event_line)** -![The solution using fs.createReadStream() and rl.readLine()](https://cdn-images-1.medium.com/max/2000/1*rhF6hIxI7aE3VsMubmVUOQ.png) +![使用 fs.createReadStream() 和 rl.readLine()](https://cdn-images-1.medium.com/max/2000/1*rhF6hIxI7aE3VsMubmVUOQ.png) -**Solution #3: [`event-stream`](https://www.npmjs.com/package/event-stream)** +**解决方案 3: [`event-stream`](https://www.npmjs.com/package/event-stream)** -![The solution using event-stream](https://cdn-images-1.medium.com/max/2000/1*WzQIXZKNvGfrZzXtEP_31g.png) +![使用 event-stream](https://cdn-images-1.medium.com/max/2000/1*WzQIXZKNvGfrZzXtEP_31g.png) -**Bonus** +**另外** -Here’s a screenshot of my `event-stream` solution churning through the 2.55GB monster file, as well. And here’s the time difference between the 400MB file and the 2.55GB file too. +这里是我的 `event-stream` 解决方案的屏幕截图,同时也是在遍历 2.55 GB 的超大文件。这里是解析 400 MB 文件和 2.55 GB 文件之间的时间差。 -![Look at those blazing fast speeds, even as the file size climbs by almost 6X.](https://cdn-images-1.medium.com/max/2548/1*Zxbn3FCHM59DrDvY7P6bXg.png) +![看看这超快的速度,即使文件大小增加了近 6 倍](https://cdn-images-1.medium.com/max/2548/1*Zxbn3FCHM59DrDvY7P6bXg.png) -**Solution #3: [`event-stream`](https://www.npmjs.com/package/event-stream) (on the 2.55GB file)** +**解决方案 3: [`event-stream`](https://www.npmjs.com/package/event-stream) (处理 2.55 GB 文件)** ![](https://cdn-images-1.medium.com/max/2000/1*v-7OzvyTjFTjrxnO0rXYiA.png) -#### Conclusion +#### 结论 -In the end, streams both native to Node.js and not, are way, WAY more efficient at processing large data sets. +最后,Node.js 原生的流和非原生的流,在处理大型数据集的时候会更加有效。 -Thanks for coming back for part 2 of my series using Node.js to read really, really large files. If you’d like to read the first blog again, you can get it [here](https://itnext.io/using-node-js-to-read-really-really-large-files-pt-1-d2057fe76b33). +感谢你继续阅读了本系列文章的第二部分。如果你想要再次阅读第一篇文章,可以看[这里](https://itnext.io/using-node-js-to-read-really-really-large-files-pt-1-d2057fe76b33)。 -I’ll be back in a couple weeks with a new JavaScript topic — possibly debugging in Node or end-to-end testing with Puppeteer and headless Chrome, so please follow me for more content. +我将在几周后回到新的 JavaScript 主题 - 可能是 Node 中的代码调试或者是使用 Puppeteer 和 Chrome 来进行端到端测试,所以请关注我来获取更多内容。 -Thanks for reading, I hope this gives you an idea of how to handle large amounts of data with Node.js efficiently and performance test your solutions. Claps and shares are very much appreciated! +感谢你的阅读,我希望这篇文章能够让你了解如何有效地处理 Node.js 的大型数据集并且对你的方案进行性能测试。非常感谢你的关注和点赞。 -**If you enjoyed reading this, you may also enjoy some of my other blogs:** +**如果你喜欢这篇文章,你也许也会喜欢我的其他博客:** -* [Using Node.js to Read Really, Really Large Datasets & Files (Pt 1)](https://itnext.io/using-node-js-to-read-really-really-large-files-pt-1-d2057fe76b33) -* [Sequelize: The ORM for Sequel Databases with Node.js](https://medium.com/@paigen11/sequelize-the-orm-for-sql-databases-with-nodejs-daa7c6d5aca3) -* [Why a Spring Cloud Config Server is Crucial to a Good CI/CD Pipeline and How To Set It Up (Pt 1)](https://medium.com/@paigen11/why-a-cloud-config-server-is-crucial-to-a-good-ci-cd-pipeline-and-how-to-set-it-up-pt-1-fa628a125776) +* [使用 Node.js 读取超大的数据集和文件(第一部分)](https://itnext.io/using-node-js-to-read-really-really-large-files-pt-1-d2057fe76b33) +* [Sequelize:Node.js 的数据库 ORM 工具](https://medium.com/@paigen11/sequelize-the-orm-for-sql-databases-with-nodejs-daa7c6d5aca3) +* [为什么 Spring Cloud Config Server 是一个好的 CI/CD 流的关键以及如何去进行配置(第一部分)](https://medium.com/@paigen11/why-a-cloud-config-server-is-crucial-to-a-good-ci-cd-pipeline-and-how-to-set-it-up-pt-1-fa628a125776) --- -**References and Further Resources:** +**引用及继续阅读:** * Github, Read File Repo: [https://github.com/paigen11/file-read-challenge](https://github.com/paigen11/file-read-challenge) * Node.js Documentation, File System: [https://nodejs.org/api/fs.html](https://nodejs.org/api/fs.html) From 2b5d732099863fbc11b250462563f0a9b60c0d44 Mon Sep 17 00:00:00 2001 From: LeviDing Date: Thu, 1 Aug 2019 14:02:09 +0800 Subject: [PATCH 65/68] Update effective-bloc-pattern.md --- TODO1/effective-bloc-pattern.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO1/effective-bloc-pattern.md b/TODO1/effective-bloc-pattern.md index 69ceed26341..dd07cc79712 100644 --- a/TODO1/effective-bloc-pattern.md +++ b/TODO1/effective-bloc-pattern.md @@ -73,7 +73,7 @@ class MoviesBloc { 如果你需要在初始化的时候需要一个 `context` 来初始化 BLoC 对象,那么这个方法就是在 `StatefulWidget` 中需要重写的最重要的方法。你可以将其视为初始化方法(最好仅用于 BLoC 的初始化)。你或许会说,我们有 `initState()` 方法,那么为什么我们要使用 `didChangeDependencies()` 方法。文档里面清楚地提到,从 `didChangeDependencies()` 调用 [BuildContext.inheritFromWidgetOfExactType](https://docs.flutter.io/flutter/widgets/BuildContext/inheritFromWidgetOfExactType.html) 是安全的。下面是使用这个方法的一个简单的例子: -```dart +```dart @override void didChangeDependencies() { bloc = MovieDetailBlocProvider.of(context); From 1bf3faeb99457f465bd6f693ccceaf088bd2fa0e Mon Sep 17 00:00:00 2001 From: Lucas biu <517197934@qq.com> Date: Thu, 8 Aug 2019 11:42:38 +0800 Subject: [PATCH 66/68] Update streams-for-the-win-a-performance-comparison-of-nodejs-methods-for-reading-large-datasets-pt-2.md --- ...methods-for-reading-large-datasets-pt-2.md | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/TODO1/streams-for-the-win-a-performance-comparison-of-nodejs-methods-for-reading-large-datasets-pt-2.md b/TODO1/streams-for-the-win-a-performance-comparison-of-nodejs-methods-for-reading-large-datasets-pt-2.md index 5001a165db2..800a37aedb6 100644 --- a/TODO1/streams-for-the-win-a-performance-comparison-of-nodejs-methods-for-reading-large-datasets-pt-2.md +++ b/TODO1/streams-for-the-win-a-performance-comparison-of-nodejs-methods-for-reading-large-datasets-pt-2.md @@ -3,7 +3,7 @@ > * 译文出自:[掘金翻译计划](https://github.com/xitu/gold-miner) > * 本文永久链接:[https://github.com/xitu/gold-miner/blob/master/TODO1/streams-for-the-win-a-performance-comparison-of-nodejs-methods-for-reading-large-datasets-pt-2.md](https://github.com/xitu/gold-miner/blob/master/TODO1/streams-for-the-win-a-performance-comparison-of-nodejs-methods-for-reading-large-datasets-pt-2.md) > * 译者:[lucasleliane](https://github.com/LucaslEliane) -> * 校对者: +> * 校对者:[Ultrasteve](https://github.com/Ultrasteve),[xilihuasi](https://github.com/xilihuasi) # 胜者是 Stream:NodeJS 读取大数据集合几种方法的性能比较 @@ -11,7 +11,7 @@ ![](https://cdn-images-1.medium.com/max/2000/1*fsseXIPGEhwmg6kfgXyIjA.jpeg) -如果你最近都在阅读我的文章,你应该会看到我几周前发布的一篇[博客](https://itnext.io/using-node-js-to-read-really-really-large-files-pt-1-d2057fe76b33),这篇博客讨论了使用 Node.js 来读取大型数据集的各种方法。 +如果你一直在关注我的文章,你应该会看到我几周前发布的一篇[博客](https://juejin.im/post/5d4b99eb5188257bc15d299f),这篇博客讨论了使用 Node.js 来读取大型数据集的各种方法。 令我惊讶的是,这篇博客受到了很多读者的喜爱 - 这个主题(对于我来说)似乎在很多其他的帖子、博客或者论坛上已经讨论过了,但是无论如何,它都吸引了很多人的关注。所以,感谢所有花时间来阅读这篇博客的读者!对此,我真的非常感激。 @@ -21,7 +21,7 @@ ### 上一篇文章中的挑战 -我不会详细介绍上一篇博客中的挑战和解决方案,因为你可以去阅读我的第一篇文章,了解所有的细节[这里](https://itnext.io/using-node-js-to-read-really-really-large-files-pt-1-d2057fe76b33),但是我会给你进行一下简单的介绍。 +我不会详细介绍上一篇博客中的挑战和解决方案,因为你可以去阅读我的第一篇文章,在[这里](https://juejin.im/post/5d4b99eb5188257bc15d299f)可以了解所有细节,但是我会给你进行一下简单的介绍。 这个挑战是来自于 Slack 频道的人发布的一个编码挑战,要求读取一个非常大的数据集(总共大小超过 2.5 GB),解析数据并且提取各种信息。 @@ -34,9 +34,9 @@ 这里是数据链接:​[https://www.fec.gov/files/bulk-downloads/2018/indiv18.zip](https://www.fec.gov/files/bulk-downloads/2018/indiv18.zip) -#### 三种解决方案与小数据集情况下的不同点 +#### 三种小数据集场景下的解决方案 -当我努力去实现处理大型数据集这个目标的过程中,我在 Node.js 中提出了三个解决方案。 +当我努力去实现处理大型数据集这个目标的过程中,我在 Node.js 中想到了三个解决方案。 **解决方案 1:[`fs.readFile()`](https://nodejs.org/api/fs.html#fs_fs_readfile_path_options_callback)** @@ -48,7 +48,7 @@ **解决方案 3:[`event-stream`](https://www.npmjs.com/package/event-stream)** -最后,我在 Node.js 中提出了唯一能够处理完整的 2.55 GB 的文件的解决方案。 +最后,我在想到了 Node.js 中唯一能够处理完整的 2.55 GB 的文件的解决方案。 > 有趣的是:Node.js 在任何时候,都只能够在内存中容纳 1.67 GB,之后就会抛出 JavaScript 的 `heap out of memory` 错误。 @@ -83,7 +83,7 @@ console.timeEnd('label1'); // 输出的结果类似于这样: label1 0.002ms ``` -这是我用来计算处理数据集所需要的时间的一种方法。 +这是我用来计算处理数据集所需时间的一种方法。 [**`performance-now`**](https://www.npmjs.com/package/performance-now) @@ -114,13 +114,13 @@ console.log('Performance for timing for label:' + (end — start).toFixed(3) + ' 我想同时使用 Node 的 `console.time()` 和 `performance-now`,我可以规避差异并且获得关于文件解析函数真正的执行时间的准确值。 -下面是我在每个脚本中接入 `console.time()` 以及 `performance-now` 的代码片段。这些代码只是每个函数的片段 - 相对于完整代码来说,你可以在[这里](https://github.com/paigen11/file-read-challenge)查看我的代码仓库。 +下面是我在每个脚本中接入 `console.time()` 以及 `performance-now` 的代码片段。这些代码只是每个函数的片段 - 如果你想看全部代码,你可以在[这里](https://github.com/paigen11/file-read-challenge)查看我的代码仓库。 **fs.readFile() 代码实现示例** ![](https://cdn-images-1.medium.com/max/2568/1*n48UZ77lvktwjN6IDR0x1g.png) -由于这个脚本使用 `fs.readFile()` 实现,整个文件都会在执行函数之前被读取到内存中,看起来这是最同步的代码。但是它实际上不是同步的,同步地读取文件在 Node 中有一个专用的方法,叫做 `fs.readFileSync()`,两者看起来很相似。 +由于这个脚本使用 `fs.readFile()` 实现,整个文件都会在执行函数之前被读取到内存中,这看起来很像同步代码。但是它实际上不是同步的,同步地读取文件在 Node 中有一个专用的方法,叫做 `fs.readFileSync()`,两者看起来很相似。 但是,我们很容易看到文件的总行数以及两个计时器方法,来确定执行行计数到底花费了多长时间。 @@ -188,7 +188,7 @@ console.log('Performance for timing for label:' + (end — start).toFixed(3) + ' #### 结论 -最后,Node.js 原生的流和非原生的流,在处理大型数据集的时候会更加有效。 +最后,流式处理,不论是 Node.js 原生的亦或是非原生的,在处理大型数据集的时候都会更加有效。 感谢你继续阅读了本系列文章的第二部分。如果你想要再次阅读第一篇文章,可以看[这里](https://itnext.io/using-node-js-to-read-really-really-large-files-pt-1-d2057fe76b33)。 From 9001a9ed941e163bb93c7cf3e5fff866bf943726 Mon Sep 17 00:00:00 2001 From: Lucas biu <517197934@qq.com> Date: Fri, 16 Aug 2019 19:43:43 +0800 Subject: [PATCH 67/68] Update streams-for-the-win-a-performance-comparison-of-nodejs-methods-for-reading-large-datasets-pt-2.md --- ...parison-of-nodejs-methods-for-reading-large-datasets-pt-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO1/streams-for-the-win-a-performance-comparison-of-nodejs-methods-for-reading-large-datasets-pt-2.md b/TODO1/streams-for-the-win-a-performance-comparison-of-nodejs-methods-for-reading-large-datasets-pt-2.md index 800a37aedb6..aa97a243449 100644 --- a/TODO1/streams-for-the-win-a-performance-comparison-of-nodejs-methods-for-reading-large-datasets-pt-2.md +++ b/TODO1/streams-for-the-win-a-performance-comparison-of-nodejs-methods-for-reading-large-datasets-pt-2.md @@ -48,7 +48,7 @@ **解决方案 3:[`event-stream`](https://www.npmjs.com/package/event-stream)** -最后,我在想到了 Node.js 中唯一能够处理完整的 2.55 GB 的文件的解决方案。 +最后,我想到了在 Node.js 中唯一能够处理完整的 2.55 GB 的文件的解决方案。 > 有趣的是:Node.js 在任何时候,都只能够在内存中容纳 1.67 GB,之后就会抛出 JavaScript 的 `heap out of memory` 错误。 From 25b8e810a8629d356f22880ba32339512b1f8c78 Mon Sep 17 00:00:00 2001 From: lsvih Date: Sun, 25 Aug 2019 16:59:07 +0800 Subject: [PATCH 68/68] =?UTF-8?q?=E4=B8=BA=E4=BB=A3=E7=A0=81=E5=9D=97?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=A0=87=E7=AD=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...son-of-nodejs-methods-for-reading-large-datasets-pt-2.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/TODO1/streams-for-the-win-a-performance-comparison-of-nodejs-methods-for-reading-large-datasets-pt-2.md b/TODO1/streams-for-the-win-a-performance-comparison-of-nodejs-methods-for-reading-large-datasets-pt-2.md index aa97a243449..947c00aa9fd 100644 --- a/TODO1/streams-for-the-win-a-performance-comparison-of-nodejs-methods-for-reading-large-datasets-pt-2.md +++ b/TODO1/streams-for-the-win-a-performance-comparison-of-nodejs-methods-for-reading-large-datasets-pt-2.md @@ -70,7 +70,7 @@ Node.js 有一些方便的内置方法,可以用于定时和性能测试,分别是 `console.time()` 和 `console.timeEnd()`。要使用这些方法,我只需要为 `time()` 和 `timeEnd()` 传递相同的 label 参数,就像下面这样,Node 就会在函数执行完成之后,输出两者之间的时间差。 -``` +```js // 定时器启动 console.time('label1'); @@ -93,7 +93,7 @@ console.timeEnd('label1'); 将 `performance-now` 模块引入到我的文件中,几乎和原生 Node.js 方法一样简单。导入模块,设置方法执行开始和执行结束的结果设置变量,并且计算两者之间的时间差。 -``` +```js // 在文件开头导入 performance-now 模块 const now = require('performance-now'); @@ -182,7 +182,7 @@ console.log('Performance for timing for label:' + (end — start).toFixed(3) + ' ![看看这超快的速度,即使文件大小增加了近 6 倍](https://cdn-images-1.medium.com/max/2548/1*Zxbn3FCHM59DrDvY7P6bXg.png) -**解决方案 3: [`event-stream`](https://www.npmjs.com/package/event-stream) (处理 2.55 GB 文件)** +**解决方案 3: [`event-stream`](https://www.npmjs.com/package/event-stream)(处理 2.55 GB 文件)** ![](https://cdn-images-1.medium.com/max/2000/1*v-7OzvyTjFTjrxnO0rXYiA.png)