Skip to content

Update translation of 2-ui/5-loading/02-script-async-defer, 03-onload-onerror and other files #705

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 23 commits into from
Apr 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion 1-js/01-getting-started/1-intro/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ JavaScript 的能力很大程度上依赖于它执行的环境。例如:[Node.
这就是“同源策略”。为了解决“同源策略”问题,两个标签页必须**都**包含一些处理这个问题的特殊的 JavaScript 代码,并均允许数据交换,这样才能够实现两个同源标签页的数据交换。本教程会讲到这部分相关的知识。

这个限制也是为了用户的信息安全。例如,用户打开的 `http://anysite.com` 网页的 JavaScript 肯定不能访问 `http://gmail.com`(另外一个标签页打开的网页)也不能从那里窃取信息。
- JavaScript 通过互联网可以轻松地和当前网页域名的服务器进行通讯。但是从其他网站/域名的服务器中获取数据的能力是受限的。尽管这可以实现,但是需要来自远程服务器的明确协议(在 HTTP 头中)。这也是为了用户的数据安全。
- JavaScript 通过互联网可以轻松地和当前网页域名的服务器进行通讯。但是从其他网站/域名的服务器中获取数据的能力是受限的。尽管这可以实现,但是需要来自远程服务器的明确协议(在 HTTP header 中)。这也是为了用户的数据安全。

![](limitations.svg)

Expand Down
4 changes: 2 additions & 2 deletions 1-js/03-code-quality/02-coding-style/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ function pow(x, n) {

## 函数位置

如果你正在写几个“辅助类”的函数和一些使用它们的代码,那么有三种方式来组织这些函数。
如果你正在写几个“辅助”函数和一些使用它们的代码,那么有三种方式来组织这些函数。

1. 在调用这些函数的代码的 **上方** 声明这些函数:

Expand Down Expand Up @@ -256,7 +256,7 @@ function pow(x, n) {
setHandler(elem);
walkAround();

// --- *!*辅助类函数*/!* ---
// --- *!*辅助函数*/!* ---
function createElement() {
...
}
Expand Down
2 changes: 1 addition & 1 deletion 1-js/06-advanced-functions/06-function-object/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,6 @@ welcome(); // Hello, Guest(嵌套调用有效)

此外,函数可以带有额外的属性。很多知名的 JavaScript 库都充分利用了这个功能。

它们创建一个“主”函数,然后给它附加很多其它 "helper" 函数。例如,[jQuery](https://jquery.com) 库创建了一个名为 `$` 的函数。[lodash](https://lodash.com) 库创建一个 `_` 函数,然后为其添加了 `_.add`、`_.keyBy` 以及其它属性(欲了解详情,参见 [docs](https://lodash.com/docs))。实际上,它们这么做是为了减少对全局空间的污染,这样一个库就只会有一个全局变量。这样就降低了命名冲突的可能性。
它们创建一个“主”函数,然后给它附加很多其它“辅助”函数。例如,[jQuery](https://jquery.com) 库创建了一个名为 `$` 的函数。[lodash](https://lodash.com) 库创建一个 `_` 函数,然后为其添加了 `_.add`、`_.keyBy` 以及其它属性(欲了解详情,参见 [docs](https://lodash.com/docs))。实际上,它们这么做是为了减少对全局空间的污染,这样一个库就只会有一个全局变量。这样就降低了命名冲突的可能性。

所以,一个函数本身可以完成一项有用的工作,还可以在自身的属性中附带许多其他功能。
2 changes: 1 addition & 1 deletion 1-js/11-async/03-promise-chaining/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ JavaScript 检查在 `(*)` 行中由 `.then` 处理程序(handler)返回的
let promise = fetch(url);
```

执行这条语句,向 `url` 发出网络请求并返回一个 promise。当远程服务器返回响应头(是在 **全部响应加载完成前**)时,该 promise 用使用一个 `response` 对象来进行 resolve。
执行这条语句,向 `url` 发出网络请求并返回一个 promise。当远程服务器返回 header(是在 **全部响应加载完成前**)时,该 promise 用使用一个 `response` 对象来进行 resolve。

为了读取完整的响应,我们应该调用 `response.text()` 方法:当全部文字(full text)内容从远程服务器下载完成后,它会返回一个 promise,该 promise 以刚刚下载完成的这个文本作为 result 进行 resolve。

Expand Down
2 changes: 1 addition & 1 deletion 1-js/13-modules/02-import-export/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ export {default as User} from './user.js'; // 重新导出 default

为什么要这样做?我们看一个实际开发中的用例。

想象一下,我们正在编写一个 "package":一个包含大量模块的文件夹,其中一些功能是导出到外部的(像 NPM 这样的工具允许发布和分发这样的 package),并且其中一些模块仅仅是供其他 package 中的模块内部使用的“帮助器(helper)”
想象一下,我们正在编写一个 "package":一个包含大量模块的文件夹,其中一些功能是导出到外部的(像 NPM 这样的工具允许发布和分发这样的 package),并且其中一些模块仅仅是供其他 package 中的模块内部使用的 "helpers"

文件结构可能是这样的:
```
Expand Down
2 changes: 1 addition & 1 deletion 1-js/13-modules/03-modules-dynamic-imports/article.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# 动态导入(Dynamic import)
# 动态导入

我们在前面章节中介绍的导出和导入语句称为“静态”导入。语法非常简单且严格。

Expand Down
2 changes: 1 addition & 1 deletion 2-ui/1-document/05-basic-dom-node-properties/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ tagName 和 nodeName 之间有什么不同吗?
如果我们只处理元素,那么 `tagName` 和 `nodeName` 这两种方法,我们都可以使用,没有区别。

```smart header="标签名称始终是大写的,除非是在 XML 模式下"
浏览器有两种处理文档(document)的模式:HTML 和 XML。通常,HTML 模式用于网页。只有在浏览器接收到带有 `Content-Type: application/xml+xhtml` 报头(header)的 XML-document 时,XML 模式才会被启用。
浏览器有两种处理文档(document)的模式:HTML 和 XML。通常,HTML 模式用于网页。只有在浏览器接收到带有 header `Content-Type: application/xml+xhtml` 的 XML-document 时,XML 模式才会被启用。

在 HTML 模式下,`tagName/nodeName` 始终是大写的:它是 `BODY`,而不是 `<body>` 或 `<BoDy>`。

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ importance: 5

---

# 图片库
# 图册

创建一个图片库,通过点击缩略图可以更改主图像
创建一个图册,通过点击缩略图可以更改主图片

像这样:

Expand Down
97 changes: 49 additions & 48 deletions 2-ui/5-loading/02-script-async-defer/article.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@

# Script 标签属性:async, defer
# 脚本:asyncdefer

现代的网站中,脚本往往比 HTML 更“重”:它们的大小通常更大,处理时间也更长。

当浏览器加载 HTML 时遇到 `<script>...</script>` 标签,浏览器就不能继续构建 DOM。它必须立刻执行此脚本。对于外部脚本 `<script src="..."></script>` 也是一样的:浏览器必须等脚本下载完,并执行结束,之后才能继续处理剩余的页面。

这会导致两个重要的问题:

1. 脚本不能访问到位于它们下面的 DOM 元素,因此,脚本不能给它们添加事件等
2. 如果页面顶部有一个庞大的脚本,它会“阻塞页面”。在脚本下载并执行结束前,用户都不能看到页面内容:
1. 脚本不能访问到位于它们下面的 DOM 元素,因此,脚本无法给它们添加处理程序等
2. 如果页面顶部有一个笨重的脚本,它会“阻塞页面”。在该脚本下载并执行结束前,用户都不能看到页面内容:

```html run height=100
<p>...content before script...</p>

<script src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script>

<!-- 在脚本加载结束前都看不到下面的内容 -->
<!-- This isn't visible until the script loads -->
<p>...content after script...</p>
```

Expand All @@ -29,17 +29,17 @@
</body>
```

但是这样的方案绝非完美。例如:浏览器只有在下载完整的 HTML 文档后才会注意到脚本(并且开始下载它)。对于长的 HTML 文档来说,这样的延迟必须引起注意
但是这种解决方案远非完美。例如,浏览器只有在下载了完整的 HTML 文档之后才会注意到该脚本(并且可以开始下载它)。对于长的 HTML 文档来说,这样可能会造成明显的延迟

对于网络连接很快的人来说,这不值一提。但是这个世界上仍然有很多地区的人们他们使用很慢的网络连接,并且使用着远非完美的移动互联网
这对于使用高速连接的人来说,这不值一提,他们不会感受到这种延迟。但是这个世界上仍然有很多地区的人们所使用的网络速度很慢,并且使用的是远非完美的移动互联网连接

幸运的是,这里有两个 `<script>` 属性可以解决我们的这个问题:`defer` 和 `async`。
幸运的是,这里有两个 `<script>` 特性(attribute)可以为我们解决这个问题:`defer` 和 `async`。

## defer

`defer` 属性告诉浏览器它应该继续处理页面,并在“后台”下载脚本,然后等页面处理完成后才开始执行此脚本
`defer` 特性告诉浏览器它应该继续处理页面,并“在后台”下载脚本,然后等页面加载完成后,再执行此脚本

接下来的这个例子和上面一样,但是是用 `defer` 属性
这是与上面那个相同的示例,但是带有 `defer` 特性

```html run height=100
<p>...content before script...</p>
Expand All @@ -50,10 +50,10 @@
<p>...content after script...</p>
```

- 具有 `defer` 属性的脚本不会阻塞页面的加载
- 具有 `defer` 属性的脚本总是要等到 DOM 解析完毕,但在 `DOMContentLoaded` 事件之前执行。
- 具有 `defer` 特性的脚本不会阻塞页面
- 具有 `defer` 特性的脚本总是要等到 DOM 解析完毕,但在 `DOMContentLoaded` 事件之前执行。

下面的例子演示了这一过程
下面这个示例演示了这一过程

```html run height=100
<p>...content before scripts...</p>
Expand All @@ -68,40 +68,40 @@
```

1. 页面内容立即显示。
2. `DOMContentLoaded` 在等待 defer 脚本动作的完成。它仅在脚本 `(2)` 下载且执行结束后才被触发
2. `DOMContentLoaded` 等待具有 `defer` 特性的脚本执行完成。`DOMContentLoaded` 仅在脚本 `(2)` 下载且执行结束后才会被触发

Defer 脚本保持他们的相对顺序,就像常规脚本一样。
具有 `defer` 特性的脚本保持其相对顺序,就像常规脚本一样。

所以,如果我们有一个长脚本在前,一个短脚本在后,那么后者就会等待前者。
因此,如果我们有一个长脚本在前,一个短脚本在后,那么后者就会等待前者。

```html
<script defer src="https://javascript.info/article/script-async-defer/long.js"></script>
<script defer src="https://javascript.info/article/script-async-defer/small.js"></script>
```

```smart header="短脚本先下载完成,但是后执行"
浏览器解析页面找到 script 属性并并行下载它们,以提高性能。因此,在上面的实例中,两个脚本并行下载。`small.js` 可能会先下载完成。
浏览器扫描页面寻找脚本,然后并行下载它们,以提高性能。因此,在上面的示例中,两个脚本是并行下载的。`small.js` 可能会先下载完成。

但是规范要求脚本按照文档顺序执行,因此它要等到 `long.js` 执行结束才会被执行。
但是规范要求脚本按照文档顺序执行,因此,它需要等到 `long.js` 执行结束才会被执行。
```

```smart header="`defer` 属性仅适用于外部脚本"
`defer` 属性会忽略没有 `src` 属性的 `<script>` 脚本
```smart header="`defer` 特性仅适用于外部脚本"
如果 `<script>` 脚本没有 `src`,则会忽略 `defer` 特性
```


## async

`async` 属性意味着脚本是完全独立的
`async` 特性意味着脚本是完全独立的

- 页面不会等待异步脚本,它会继续处理页面并显示内容
- `DOMContentLoaded` 和 async 脚本不会彼此等待
- `DOMContentLoaded` 可能发生在异步脚本之前(此时异步脚本在页面加载完成后才加载完成
- `DOMContentLoaded` 也可能发生在异步脚本之后(此时异步脚本可能很短或者是从 HTTP 缓存中加载的)
- 其他脚本不会等待 `async` 脚本加载完成,同样 `async` 脚本也不会等待其他脚本。
- 页面不会等待异步脚本,它会继续处理并显示页面内容
- `DOMContentLoaded` 和异步脚本不会彼此等待
- `DOMContentLoaded` 可能会发生在异步脚本之前(如果异步脚本在页面完成后才加载完成
- `DOMContentLoaded` 也可能发生在异步脚本之后(如果异步脚本很短,或者是从 HTTP 缓存中加载的)
- 其他脚本不会等待 `async` 脚本加载完成,同样`async` 脚本也不会等待其他脚本。


因此,如果我们有几个 `async` 脚本,它们可能按任意次序执行,总之是先加载完成的就先运行
因此,如果我们有几个 `async` 脚本,它们可能按任意次序执行。总之是先加载完成的就先执行

```html run height=100
<p>...content before scripts...</p>
Expand All @@ -116,19 +116,19 @@ Defer 脚本保持他们的相对顺序,就像常规脚本一样。
<p>...content after scripts...</p>
```

1. 页面内容立刻显示出来:`async` 脚本不会阻塞页面加载
2. `DOMContentLoaded` 可以在 `async` 之前或之后触发,不能保证谁在前谁在后
3. Async 脚本不会等待彼此。一个小的脚本 `small.js` 放在后面,但是可能会比 `long.js` 这个长脚本先加载完成,所以尽管 `small.js` 在后面,但是它可能先运行。这一行为被称为“加载优先(load-first)”顺序。
1. 页面内容立刻显示出来:`async` 脚本不会阻塞页面
2. `DOMContentLoaded` 可能在 `async` 之前或之后触发,不能保证谁先谁后
3. 异步脚本不会等待彼此。较小的脚本 `small.js` 排在第二位,但可能会比 `long.js` 这个长脚本先加载完成,所以 `small.js` 会先执行。这被称为“加载优先”顺序。

当我们将独立的第三方脚本集成到页面的时候,此时采用异步加载方式是非常棒的:计数器,广告等等,因为它们不依赖于我们的脚本,同样我们的脚本也不应该等待它们加载完成
当我们将独立的第三方脚本集成到页面时,此时采用异步加载方式是非常棒的:计数器,广告等,因为它们不依赖于我们的脚本,我们的脚本也不应该等待它们

```html
<!-- Google Analytics 脚本通常是这样嵌入页面的 -->
<script async src="https://google-analytics.com/analytics.js"></script>
```


## 动态脚本(Dynamic scripts)
## 动态脚本

我们也可以使用 JavaScript 动态地添加脚本:

Expand All @@ -138,15 +138,14 @@ script.src = "/article/script-async-defer/long.js";
document.body.append(script); // (*)
```

当脚本附加到文档 `(*)` 时,脚本就会开始加载:
当脚本被附加到文档 `(*)` 时,脚本就会立即开始加载。

**默认情况下,动态脚本表现为“异步”行为。**
**默认情况下,动态脚本的行为是“异步”。**

这也就是说
- 它们不会等待其他内容,其他的内容也不会等待它们
- 先加载完成的脚本先运行(“加载优先” 顺序)
也就是说
- 它们不会等待任何东西,也没有什么东西会等它们
- 先加载完成的脚本先执行(“加载优先”顺序)

我们可以通过将 `async` 属性显示修改为 `false` 以将加载优先顺序修改为文档顺序(就像常规脚本一样):

```js run
let script = document.createElement('script');
Expand All @@ -159,7 +158,9 @@ script.async = false;
document.body.append(script);
```

例如,这里我们添加了两个脚本。在没有设置 `script.async=false` 时,它们执行顺序为加载优先顺序(即 `small.js` 可能先运行)。但是当设置了 `script.async=false` 后,脚本执行顺序就是它在文档中的顺序:
我们可以通过将 `async` 特性显式地修改为 `false`,以将脚本的加载顺序更改为文档顺序(就像常规脚本一样):

例如,这里我们添加了两个脚本。在没有设置 `script.async=false` 时,它们执行顺序为加载优先顺序(即 `small.js` 可能先执行)。但是当设置了 `script.async=false` 后,脚本执行顺序就变成了“脚本在文档中的顺序”:


```js run
Expand All @@ -170,29 +171,29 @@ function loadScript(src) {
document.body.append(script);
}

// 由于 async=false 属性存在,long.js 会先运行
// long.js 先执行,因为代码中设置了 async=false
loadScript("/article/script-async-defer/long.js");
loadScript("/article/script-async-defer/small.js");
```


## 总结

`async` 和 `defer` 属性有一个共同点:它们都不会阻塞页面的渲染。因此,用户可以立即阅读并了解页面内容。
`async` 和 `defer` 有一个共同点:加载这样的脚本都不会阻塞页面的渲染。因此,用户可以立即阅读并了解页面内容。

但是它们之间也存在一些本质的区别
但是,它们之间也存在一些本质的区别

| 类型 | 顺序 | `DOMContentLoaded` |
| | 顺序 | `DOMContentLoaded` |
|---------|---------|---------|
| `async` | **加载优先顺序**。脚本在文档中的顺序不重要 —— 先加载完成先运行 | 无关紧要。可能在文档还未完全下载前加载执行。如果脚本很小或者来自于缓存,同时文档又足够长,就会发生这种情况。|
| `defer` | **文档顺序**(它们在文档中的位置) | `DOMContentLoaded` 之前且在文档加载解析之后执行(可能需要等待)。|
| `async` | **加载优先顺序**。脚本在文档中的顺序不重要 —— 先加载完成先执行 | 不相关。可能在文档加载完成前加载并执行完毕。如果脚本很小或者来自于缓存,同时文档足够长,就会发生这种情况。 |
| `defer` | **文档顺序**(它们在文档中的顺序) | 在文档加载和解析完成之后(如果需要,则会等待),即在 `DOMContentLoaded` 之前执行。 |

```warn header="没有脚本的页面应该也是可用的"
请注意,如果你使用的是 `defer`,那么在脚本加载之前页面都是“可见”
请注意,如果你使用的是 `defer`,那么该页面在脚本加载之前就“可见”。

因此,用户可以阅读这个页面内容,但是一些图形组件可能没有准备完成
因此,用户可以阅读这个页面的内容,但是某些图形组件可能尚未准备好

所以,这就需要在页面适当位置添加“加载”进度指示,禁用无效的按钮,以清楚地向用户显示什么准备好了什么没有准备好
所以,我们应该在页面适当位置添加“正在加载”的指示,并且被禁用的按钮也应该这样显示,这样用户就可以清晰地看到什么准备好了,什么还没准备好
```

在开发中,通常在脚本需要整个 DOM 文档或者脚本的相对执行顺序很重要的时候,使用 `defer` 属性。而当脚本之间互相独立,比如计数器或者广告,并且它们相对执行顺序不重要的时候,此时使用 `async` 属性
在实际开发中,`defer` 用于需要整个 DOM 的脚本,和/或脚本的相对执行顺序很重要的时候。`async` 用于独立脚本,例如计数器或广告,这些脚本的相对执行顺序无关紧要
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

算法:
1. 为每个资源制作 `img`。
2. 为每个 image 添加 `onload/onerror`。
3. 在 `onload` 或 `onerror` 被触发时增加计数器
4. 当计数器值等于资源值时 —— 结束:`callback()`。
1. 为每个资源创建 `img`。
2. 为每个图片添加 `onload/onerror`。
3. 在 `onload` 或 `onerror` 被触发时,增加计数器
4. 当计数器值等于资源值时 —— 我们完成了:`callback()`。
Loading