JavaScript DOM 编程艺术(第2版)读书笔记(JavaScript的最佳实践)

by admin on 2020年3月5日

有网站管理者发文指责全球知名域名注册商 GoDaddy
在未经其授权的情况下,私自在其所有网页中植入 JavaScript
脚本,而这可能导致网站性能下降甚至崩溃。

1、防止滥用JavaScript“不管你想通过JavaScript改变哪个网页的行为,都必须三思而后行。首先要确认:为这个网页增加这种额外的行为是否确有必要?”

写在前面

该网友的网站托管于 GoDaddy,在检查网站时,他发现了无法识别的 JavaScript
映射文件,这意味着有不明 JavaScript
代码在其网站上加载,而且引发了一系列警报,于是他深入分析原因。

个人认为,作者的这句话放在当前几乎无处不用JavaScript来增强Web页面交互体验的时代,可以理解为应该适当的使用JavaScript,而不要因为使用了实现酷炫效果的脚本导致网页加载缓慢或者兼容性极差而舍本求末,导致用户无法浏览和使用网站。

JavaScript在浏览器中的性能,可认为是开发者所要面对的最重要的可用性的问题,此问题因JavaScript的阻塞特征而复杂,也就是说JavaScript运行时其他的事情不能被浏览器处理,事实上,大多数浏览器使用单进程处理UI更新和JavaScript运行等多个任务,而同一时间只能有一个任务被执行。JavaScript运行了多长时间,那么浏览器空闲下来响应用户输入之前的等待时间就有多长。

最后,他发现网站上所有页面都在闭合 </html>
之前注入了一段 <script>:

2、平稳退化平稳退化是指当用户禁用浏览器JavaScript或浏览器不支持JavaScript(还有吗)时,应该让用户仍然可以正常的浏览网站。

 

<script>'undefined'=== typeof _trfq || (window._trfq = []);'undefined'=== typeof _trfd && (window._trfd=[]),_trfd.push({'tccl.baseHost':'secureserver.net'}),_trfd.push({'ap':'cpsh'},{'server':'xxxxxxxx0000'}) // Monitoring performance to make your website faster. If you want to opt-out, please contact web hosting support.</script><script src='https://img1.wsimg.com/tcc/tcc_l.combined.1.0.6.min.js'></script>

刚看到这个问题的时候,感觉这种情况几乎可以忽略不计,因此看了一些关于这个问题的讨论(知乎)以及博文《javascript不可用的问题探究》,并拿博客园首页做了实验,即在禁用JavaScript的情况下浏览网页,发现虽然无法评论博客、无法正常显示分类效果、当然广告也没了,但是主要的功能(查看博文、分页跳转等)是可以正常使用的。

从基本层面说,这就意味着<script>标签的出现使整个页面因脚本解析、运行出现等待。不论实际的JavaScript代码是内联的还是包含在一个不相干的外部文件中页面下载和解析过程必须停下,等待脚本完成这些处理,然后才能继续,也是页面生命周期必不可少的部分,因为脚本可能在运行过程中修改页面内容。

实际上这是一种名为 Real User Metrics(RUM)的技术,它的 JavaScript
代码片段用于衡量和跟踪用户网站的性能,并收集连接时间和页面加载时间等信息,用于改进系统、优化
DNS 解析、改善网络路由和服务器配置。

那么基本可以得出结论:在考虑平稳退化的时候,根据需要至少应该保证网站主要功能可以正常使用。对博客园来说,就是查看博客。

 

GoDaddy 在其帮助文档中表示“绝大部分用户对此不会感觉到有什么问题,但
JavaScript
脚本本身可能会导致网站加载缓慢或破坏网页”,而文章作者表示愤怒的地方就在这里,一方面,GoDaddy
明知道 RUM
会让用户的网站出问题,而另一方面它又没有让用户有足够的选择权,而是选择以“侵犯
Web 主机与用户之间的信任”的方式悄悄使用该技术。

3、禁用”javascript:”伪协议和内嵌事件处理函数虽然在HTML中使用这两种写法,不会带来什么严重问题,但是它会阻止平稳退化(网页行为不一致),并且使脚本的编写方式混乱,增加了代码维护的难度。

在加载JavaScript过程中,页面解析和用户交互是被完全阻塞的。

值得一提的是,作者在讲到 GoDaddy 承认 JavaScript
脚本会对网站造成损害的时候,强势嘲讽了一下 GoDaddy
这个所谓用于提高性能和可靠性的 RUM:

4、性能考虑关于性能考虑的最佳实践,还是非常容易理解的。

 

So much for a tool that is designed to improve performance and
reliability!

“尽量少访问DOM和尽量减少标记”。少访问DOM是因为查询DOM的操作会非常耗费性能。多处函数的重复DOM查询应该进行重构,提取为全局变量或者直接作为参数进行传递。减少标记可以减小DOM的规模,从而减少查找DOM树中特定元素的时间。

脚本位置

该 RUM 服务可以禁用,方法:找到主机控制台,单击右上角的“…”,单击“Help
Us”。

“合并脚本”。合并外部脚本文件,可以减少加载页面时发送的请求次数。通过观察Chrome开发者工具Network标签,可以非常清楚的看到加载网页的请求次数和时间,下图为请求我的博客的情况,第三方的百度分享插件是最慢被加载的,当然整体加载时间还是可以接受的。不过当引用的文件过多过大,或者依赖的第三方插件请求缓慢时,整个页面就会一直处于加载状态,给人的感觉就是网页加载缓慢,体验就会变差。

 HTML 4 文档指出,一个<script>标签可以放在 HTML文档的 <head>
或者<body>标签中,可以在其中多次出现。传统上,<script>
标签用于加载外部JavaScript 文件。<head>部分除此类代码外,还包含
<link>标签用于加载外部css文件和其他页面中间件。也就是说,最好把风格和行为所依赖的部分放在一起,首先加载他们,使他们可以得到正确的外观和行为。

图片 1

图片 2

 

在弹出的窗口中单击“Opt Out”。

另外,“脚本在标记中的位置对页面的初次加载时间也有很大的影响”。就像bootstrap的实例中,引用的JS插件统统放在了页面的尾部,并加了说明。

例如:

图片 3

图片 4
因为根据HTTP规范,浏览器每次从同一个域名中最多只能同时下载两个文件,而在脚本下载期间,浏览器不会下载其它任何文件,即使是来自不同域名的文件也不会下载,所有其它的资源都要等脚本加载完毕后才会下载;而通常我们会把脚本文件放在块中,此时该块中的脚本会导致浏览器无法并行加载其它文件(如图片或其它脚本)。

 

(文/开源中国)    

把所有 <script>
标签放在文档的末尾,</body>标签之前,就可以让页面变的更快,因为这样,在加载脚本时,window对象的load事件依然可以执行对文档进行各种操作。

复制代码

最后一条建议就是压缩脚本,这个见的很多,通常就是以.min.js为后缀的脚本文件。

 1 <html> 

 2 <head>

 3 <title>Script Example</title>

 4 <– Example of ineffi cient script positioning –>

 5 <script type=”text/javascript” src=”file1.js”></script>

 6 <script type=”text/javascript” src=”file2.js”></script>

 7 <script type=”text/javascript” src=”file3.js”></script>

 8 <link rel=”stylesheet” type=”text/css” href=”styles.css”>

 9 </head>

10 <body>

11 <p>Hello world!</p>

12 </body>

13 </html>

复制代码

 

虽然这些代码看起来没什么问题,但是在〈head〉部分加载了三个JavaScript文件。每个〈script〉标签阻塞了页面解析过程,直到完整的下载并运行了外部JavaScript代码之后,页面才能继续进行。在浏览器没有遇到〈body〉标签之前,不会渲染页面的任何部分。

 

把脚本放在页面的顶端,将会导致一个可以察觉的延迟,通常表现为:页面打开一片白,用户不能阅读和操作。

 

 

 

如图,当第一javas文件开始下载时,阻塞了其他文件下载。进一步当第一个文件下载完成之后和第二个文件下载之前有一个延时,是第一个文件完全运行所需要的时间。

 

解决这个问题推荐的办法是:将所有<script>
标签放在尽可能接近<body>
标签的底部位置,尽量减少对整个页面下载的影响。

 

如:

 

复制代码

 1 <html>

 2 <head>

 3 <title>Script Example</title>

 4 <link rel=”stylesheet” type=”text/css” href=”styles.css”>

 5 </head>

 6 <body>

 7 <p>Hello world!</p>

 8 <– Example of recommended script positioning –>

 9 <script type=”text/javascript” src=”file1.js”></script>

10 <script type=”text/javascript” src=”file2.js”></script>

11 <script type=”text/javascript” src=”file3.js”></script>

12 </body>

13 </html>

复制代码

组成脚本

 由于每个<script>标签下载时阻塞页面解析过程,所以限制页面的<script>总数也是可以改善性能。这个规则对内联脚本和外部脚本同样适用。每当页面解析碰到一个<script>标签时,紧接着有一段时间用于代码执行。最小化这些延迟时间可以改善页面的整体性能。

 

每个HTTP请求都会产生额外的性能负担,下载一个100KB的文件比下载4个25KB的文件要快。总之,减少引用外部文件的数量。典型的,一个大型网站或者网页应用需要多次请求JavaScript文件。你可以将这些文件整合成一个文件,只需要一个<script>标签引用,就可以减少性能损失。

 

非阻塞脚本

 JavaScript倾向于阻塞浏览器某些处理过程,如HTTP请求和界面刷新,这是开发者面临的最显著的性能问题。保持JavaScript文件短小,并限制HTTP请求的数量,只是创建反应迅速的网页应用的第一步。一个应用程序所包含的功能越多,所需要的JavaScript代码就越大,保持源码短小并不总是一种选择。尽可能下载一个大JavaScript文件只产生一次HTTP请求。却会锁住浏览器一大段时间。为避开这种情况,你需要向页面中逐步添加JavaScript,某种程度上说不会阻塞浏览器。

 

非阻塞脚本的秘密在于,等页面加载之后,再加载JavaScript源码。从技术角度上讲,这意味着在window的load事件发出之后下载代码。有几种方法可以实现这种效果。

 

1.延期脚本

 

HTML4为<script>标签定义了一个扩展属性:defer。这个defer属性指明元素中所包含的脚本不打算修改DOM,因此代码可以稍后执行(适用于IE4以上浏览器)

 

<script type=”text/javascript” src=”file1.js”
defer></script>

带有该属性的JavaScript文件在<script>被解析时启动下载,但代码不会被执行,直到DOM加载完成,它不会阻塞浏览器的其他处理过程,所以这些文件可以与页面的其他资源一起并行下载。

2.动态脚本元素

 

文档对象模型dom允许使用JavaScript动态创建HTML的几乎全部文档内容。其根本在于<script>元素与页面其他元素没有什么不同。

 当文件使用动态脚本节点下载时,返回的代码通常立即执行。当脚本“自运行”类型时这一机制运行正常,但是如果脚本只包含页面其他脚本调用的的接口,则会带来问题。这种情况下,你需要跟踪脚本下载完成并准备妥善的情况。

IE
会发出一个readystatechange事件。<script>元素有一个readyState属性,它的值随着外部下载的过程而改变。readyState有5种取值。

uninitialized       默认状态

loading             开始下载

interactive        下载完成但尚不可用

complete          所有数据都已经准备好

 

下面封装一个函数来实现JavaScript文件的动态加载:

 

复制代码

 1 function loadScript (url, callback){

 2 var script = document.createElement (“script”)

 3 script.type = “text/javascript”;

 4 if (script.readyState){ //IE

 5 script.onreadystatechange = function(){

 6 if (script.readyState == “loaded” ||  script.readyState ==
“complete”){

 7 script.onreadystatechange = null;

 8 callback();

 9 }

10 };

11 } else {  //Others

12 script.onload = function(){

13 callback();

14 };

15 }

16 script.src = url;

17 document.getElementsByTagName_r(“head”)[0].appendChild(script);

18 }

复制代码

使用方法:

 

1 loadScript(“file1.js”, function(){

2 alert(“File is loaded!”);

3 }); 

使文件按顺序加载:

 

复制代码

1 loadScript(“file1.js”, function(){

2 loadScript(“file2.js”, function(){

3 loadScript(“file3.js”, function(){

4 alert(“All files are loaded!”);

5 });

6 });

7 });

复制代码

3.XHR脚本注入

使用XMLHttpRequest(XHR)对象将脚本注入到页面中。此技术首先创建一个XHR对象,然后下载javas文件,接着用一个动态<script>元素将javas代码注入页面。

 

复制代码

 1 var xhr = new XMLHttpRequest();

 2 xhr.open(“get”, “file1.js”, true);

 3 xhr.onreadystatechange = function(){

 4 if (xhr.readyState == 4){

 5 if (xhr.status >= 200 && xhr.status < 300 | | xhr.status ==
304){

 6 var script = document.createElement (“script”);

 7 script.type = “text/javascript”;

 8 script.text = xhr.responseText;

 9 document.body.appendChild(script);

10 }

11 }

12 };

13 xhr.send(null);

复制代码

此代码向服务器发送一个获取file1.js文件的GET请求。onreadystatechange事件处理函数检查readyState是不是4,然后检查HTTP状态码是不是有效(2XX表示有效回应,304表示一个缓存响应)。如果收到一个有效的响应,那么就创建一个新的<script>元素,将它的文本属性设置为从服务器接受到的resposeText字符串。这样做实际上会创建一个带有内联代码的<script>元素。一旦新的<script>元素被添加到文档,代码将被执行并准备使用。

 

这种方法的主要优点是,您可以下载不立即执行的 JavaScript
代码。由于代码返回在<script>标签之外(换句话说不受<script>标签约束),它下载后不会自动执行,这使得您可以推迟执行,直到一切都准备好了。另一个优点是,同样的代码在所有现代浏览器中都不会引发异常。 

 

此方法最主要的限制是:JavaScript 文件必须与页面放置在同一个域内,不能从
CDN 下载(CDN 指”内容投递网络(Content Delivery
Network)”,所以大型网页通常不采用 XHR 脚本注入技术。

 

总结

减少 JavaScript 对性能的影响有以下几种方法:

 

将所有的<script>标签放到页面底部,也就是</body>闭合标签之前,这能确保在脚本执行前页面已经完成了渲染。

尽可能地合并脚本。页面中的<script>标签越少,加载也就越快,响应也越迅速。无论是外链脚本还是内嵌脚本都是如此。

采用无阻塞下载 JavaScript 脚本的方法:

使用<script>标签的 defer 属性(仅适用于 IE 和 Firefox 3.5
以上版本);

使用动态创建的<script>元素来下载并执行代码;

使用 XHR 对象下载 JavaScript 代码并注入页面中。

JavaScript在浏览器中的性能,可认为是开发者所要面对的最重要的可用性的问题,此问题因JavaScript的阻塞特征而复杂,也就是说J…

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图