@@ -120,17 +120,19 @@ What time is it?
120
120
121
121
` javascript: ` 协议的常见用途是书签脚本Bookmarklet。由于浏览器的书签保存的是一个网址,所以` javascript: ` 网址也可以保存在里面,用户选择这个书签的时候,就会在当前页面执行这个脚本。为了防止书签替换掉当前文档,可以在脚本最后返回` void 0 ` 。
122
122
123
- ## ` < script > ` 标签的工作原理
123
+ ## script标签
124
124
125
- 正常的网页加载流程是这样的。
125
+ ### 工作原理
126
+
127
+ 浏览器加载JavaScript脚本,主要通过` < script> ` 标签完成。正常的网页加载流程是这样的。
126
128
127
129
1. 浏览器一边下载HTML网页,一边开始解析
128
130
2. 解析过程中,发现` < script> ` 标签
129
131
3. 暂停解析,网页渲染的控制权转交给JavaScript引擎
130
132
4. 如果` < script> ` 标签引用了外部脚本,就下载该脚本,否则就直接执行
131
133
5. 执行完毕,控制权交还渲染引擎,恢复往下解析HTML网页
132
134
133
- 也就是说, 加载外部脚本时,浏览器会暂停页面渲染,等待脚本下载并执行完成后,再继续渲染。原因是JavaScript可以修改DOM(比如使用` document .write ` 方法),所以必须把控制权让给它,否则会导致复杂的线程竞赛的问题。
135
+ 加载外部脚本时,浏览器会暂停页面渲染,等待脚本下载并执行完成后,再继续渲染。原因是JavaScript可以修改DOM(比如使用` document .write ` 方法),所以必须把控制权让给它,否则会导致复杂的线程竞赛的问题。
134
136
135
137
如果外部脚本加载时间很长(比如一直无法完成下载),就会造成网页长时间失去响应,浏览器就会呈现“假死”状态,这被称为“阻塞效应”。
136
138
@@ -185,7 +187,7 @@ What time is it?
185
187
</body >
186
188
```
187
189
188
- 如果有多个script标签 ,比如下面这样。
190
+ 如果有多个 ` script ` 标签 ,比如下面这样。
189
191
190
192
``` html
191
193
<script src =" a.js" ></script >
@@ -202,7 +204,7 @@ Gecko和Webkit引擎在网页被阻塞后,会生成第二个线程解析文档
202
204
203
205
此外,对于来自同一个域名的资源,比如脚本文件、样式表文件、图片文件等,浏览器一般最多同时下载六个(IE11允许同时下载13个)。如果是来自不同域名的资源,就没有这个限制。所以,通常把静态文件放在不同的域名之下,以加快下载速度。
204
206
205
- ## defer属性
207
+ ### defer属性
206
208
207
209
为了解决脚本文件下载阻塞网页渲染的问题,一个方法是加入defer属性。
208
210
@@ -222,7 +224,7 @@ Gecko和Webkit引擎在网页被阻塞后,会生成第二个线程解析文档
222
224
223
225
对于内置而不是连接外部脚本的script标签,以及动态生成的script标签,` defer ` 属性不起作用。
224
226
225
- ## async属性
227
+ ### async属性
226
228
227
229
解决“阻塞效应”的另一个方法是加入` async ` 属性。
228
230
@@ -245,62 +247,7 @@ Gecko和Webkit引擎在网页被阻塞后,会生成第二个线程解析文档
245
247
246
248
一般来说,如果脚本之间没有依赖关系,就使用` async ` 属性,如果脚本之间有依赖关系,就使用` defer ` 属性。如果同时使用` async ` 和` defer ` 属性,后者不起作用,浏览器行为由` async ` 属性决定。
247
249
248
- ## 重流和重绘
249
-
250
- 渲染树转换为网页布局,称为“布局流”(flow);布局显示到页面的这个过程,称为“绘制”(paint)。它们都具有阻塞效应,并且会耗费很多时间和计算资源。
251
-
252
- 页面生成以后,脚本操作和样式表操作,都会触发重流(reflow)和重绘(repaint)。用户的互动,也会触发,比如设置了鼠标悬停(` a:hover ` )效果、页面滚动、在输入框中输入文本、改变窗口大小等等。
253
-
254
- 重流和重绘并不一定一起发生,重流必然导致重绘,重绘不一定需要重流。比如改变元素颜色,只会导致重绘,而不会导致重流;改变元素的布局,则会导致重绘和重流。
255
-
256
- 大多数情况下,浏览器会智能判断,将重流和重绘只限制到相关的子树上面,最小化所耗费的代价,而不会全局重新生成网页。
257
-
258
- 作为开发者,应该尽量设法降低重绘的次数和成本。比如,尽量不要变动高层的DOM元素,而以底层DOM元素的变动代替;再比如,重绘` table ` 布局和` flex ` 布局,开销都会比较大。
259
-
260
- ``` javascript
261
- var foo = document .getElementById (' foobar' );
262
-
263
- foo .style .color = ' blue' ;
264
- foo .style .marginTop = ' 30px' ;
265
- ```
266
-
267
- 上面的代码只会导致一次重绘,因为浏览器会累积DOM变动,然后一次性执行。
268
-
269
- 下面是一些优化技巧。
270
-
271
- - 读取DOM或者写入DOM,尽量写在一起,不要混杂
272
- - 缓存DOM信息
273
- - 不要一项一项地改变样式,而是使用CSS class一次性改变样式
274
- - 使用document fragment操作DOM
275
- - 动画时使用absolute定位或fixed定位,这样可以减少对其他元素的影响
276
- - 只在必要时才显示元素
277
- - 使用` window.requestAnimationFrame() ` ,因为它可以把代码推迟到下一次重流时执行,而不是立即要求页面重流
278
- - 使用虚拟DOM(virtual DOM)库
279
-
280
- 下面是一个` window.requestAnimationFrame() ` 对比效果的例子。
281
-
282
- ``` javascript
283
- // 重绘代价高
284
- function doubleHeight (element ) {
285
- var currentHeight = element .clientHeight ;
286
- element .style .height = (currentHeight * 2 ) + ' px' ;
287
- }
288
-
289
- all_my_elements .forEach (doubleHeight);
290
-
291
- // 重绘代价低
292
- function doubleHeight (element ) {
293
- var currentHeight = element .clientHeight ;
294
-
295
- window .requestAnimationFrame (function () {
296
- element .style .height = (currentHeight * 2 ) + ' px' ;
297
- });
298
- }
299
-
300
- all_my_elements .forEach (doubleHeight);
301
- ```
302
-
303
- ## 脚本的动态嵌入
250
+ ### 脚本的动态嵌入
304
251
305
252
除了用静态的` script ` 标签,还可以动态嵌入` script ` 标签。
306
253
@@ -348,7 +295,7 @@ all_my_elements.forEach(doubleHeight);
348
295
349
296
此外,动态嵌入还有一个地方需要注意。动态嵌入必须等待CSS文件加载完成后,才会去下载外部脚本文件。静态加载就不存在这个问题,` script ` 标签指定的外部脚本文件,都是与CSS文件同时并发下载的。
350
297
351
- ## 加载使用的协议
298
+ ### 加载使用的协议
352
299
353
300
如果不指定协议,浏览器默认采用HTTP协议下载。
354
301
@@ -393,6 +340,61 @@ all_my_elements.forEach(doubleHeight);
393
340
394
341
以上四步并非严格按顺序执行,往往第一步还没完成,第二步和第三步就已经开始了。所以,会看到这种情况:网页的HTML代码还没下载完,但浏览器已经显示出内容了。
395
342
343
+ ### 重流和重绘
344
+
345
+ 渲染树转换为网页布局,称为“布局流”(flow);布局显示到页面的这个过程,称为“绘制”(paint)。它们都具有阻塞效应,并且会耗费很多时间和计算资源。
346
+
347
+ 页面生成以后,脚本操作和样式表操作,都会触发重流(reflow)和重绘(repaint)。用户的互动,也会触发,比如设置了鼠标悬停(` a:hover ` )效果、页面滚动、在输入框中输入文本、改变窗口大小等等。
348
+
349
+ 重流和重绘并不一定一起发生,重流必然导致重绘,重绘不一定需要重流。比如改变元素颜色,只会导致重绘,而不会导致重流;改变元素的布局,则会导致重绘和重流。
350
+
351
+ 大多数情况下,浏览器会智能判断,将重流和重绘只限制到相关的子树上面,最小化所耗费的代价,而不会全局重新生成网页。
352
+
353
+ 作为开发者,应该尽量设法降低重绘的次数和成本。比如,尽量不要变动高层的DOM元素,而以底层DOM元素的变动代替;再比如,重绘` table ` 布局和` flex ` 布局,开销都会比较大。
354
+
355
+ ``` javascript
356
+ var foo = document .getElementById (' foobar' );
357
+
358
+ foo .style .color = ' blue' ;
359
+ foo .style .marginTop = ' 30px' ;
360
+ ```
361
+
362
+ 上面的代码只会导致一次重绘,因为浏览器会累积DOM变动,然后一次性执行。
363
+
364
+ 下面是一些优化技巧。
365
+
366
+ - 读取DOM或者写入DOM,尽量写在一起,不要混杂
367
+ - 缓存DOM信息
368
+ - 不要一项一项地改变样式,而是使用CSS class一次性改变样式
369
+ - 使用document fragment操作DOM
370
+ - 动画时使用absolute定位或fixed定位,这样可以减少对其他元素的影响
371
+ - 只在必要时才显示元素
372
+ - 使用` window.requestAnimationFrame() ` ,因为它可以把代码推迟到下一次重流时执行,而不是立即要求页面重流
373
+ - 使用虚拟DOM(virtual DOM)库
374
+
375
+ 下面是一个` window.requestAnimationFrame() ` 对比效果的例子。
376
+
377
+ ``` javascript
378
+ // 重绘代价高
379
+ function doubleHeight (element ) {
380
+ var currentHeight = element .clientHeight ;
381
+ element .style .height = (currentHeight * 2 ) + ' px' ;
382
+ }
383
+
384
+ all_my_elements .forEach (doubleHeight);
385
+
386
+ // 重绘代价低
387
+ function doubleHeight (element ) {
388
+ var currentHeight = element .clientHeight ;
389
+
390
+ window .requestAnimationFrame (function () {
391
+ element .style .height = (currentHeight * 2 ) + ' px' ;
392
+ });
393
+ }
394
+
395
+ all_my_elements .forEach (doubleHeight);
396
+ ```
397
+
396
398
### JavaScript引擎
397
399
398
400
JavaScript引擎的主要作用是,读取网页中的JavaScript代码,对其处理后运行。
0 commit comments