脚本页面js脚本加载总结
2013-05-17 22:19
260 查看
废话就不多说了,开始。。。
这段时光工作工作上不是很紧,零星的在研究浏览器的一些东西,刚好这个月又一次轮到我做沙龙讲座了,想好了许久,就来一次js脚本加载的总结吧!这一块应该对于很多做项目的朋友来会有所帮助吧!
1、js来源
总所周知网页最开始的形态是静态的(也就是所谓的静态网页),那时候的网页主要用于浏览资料信息,可是随着用户需求的增长,用户希望在页面上做一些交互操纵,比如页面需要验证才能拜访、页面输入的一些数据希望下次还可以拜访等等,js的涌现满意了人们的需求。也就涌现了所谓的动态网页。
虽然js给人们带来了很好的交互性,但是起初人们乱用脚本,只是网页页面代码凌乱不堪,当css的涌现处理了这一问题:
晓得如今,我们的网页也是分为三部分:HTML(主要放置界面元素)、CSS(主要担任界面布局)和JS(主要担任界面交互)。
大家都晓得这三部分,但是有很多人不注意一些细节,致使做出的网页拜访时效率低下。在这里我将和大家探讨一下关于js的一些知识,希望给大家带来必定帮助吧!
2、三种应用脚本方法
2.1外部引用JavaScript
2.11通过HTML的script标签加载JavaScript代码
如:
2.12通过注释隐藏JavaScript代码
如:
<!-- ... //-->当浏览器不支持JavaScript时,屏蔽JavaScript代码。这个代码是骇客技术,<!-- ... -->于HTML注释,// 是JavaScript注释。当浏览器支持JavaScript时//代码生效,因此HTML的注释没有效果;当浏览器不支JavaScript时,//代码无效,因此屏蔽了<!--
... -->之间的JavaScript代码。当初这种隐藏JavaScript代码的方法可以疏忽,因为没有浏览器不支持JavaScript,除了部分用户手动制止浏览器的JavaScript功能,但是这种情况很少发生。
2.1.3应用noscript标签为用户提供更好的休会
如:
通过JavaScript注释的方法可以隐藏JavaScript代码,通过noscript标签可认为用户提供更好的休会(提示用户你的浏览器不支持JavaScript)。
2.2外部引用JavaScript
应用<script>标签的src属性来加载js脚本。平日JavaScript文件可以应用script标签加载到网页的任何一个地方,但是标准的方法是加载在head标签内。为避免网页加载迟缓,也可以把非关键的JavaScript放到网页底部。
这里有几点好处:
1)避免应用<!-- ... //-->,骇客技术。
2)统必定义JavaScript代码,方便查看,方便维护。
3)使代码更安全,可以压缩,加密单个JavaScript文件。
4)浏览器可以缓存JavaScript文件,减少宽带应用。
2.3内联引用JavaScript
内联引用是通过HTML标签中的事件属性实现的。
下面示例将调用input标签的onclick属性,弹出一个提示框。
3内外脚本的比较
内联脚本方法应用场景很少,几乎没什么优势。
外部脚本示例:http://stevesouders.com/hpws/inlined.php
外部脚本示例:http://stevesouders.com/hpws/external.php
外部脚本示例只有一个HTML文档,其大小为87kb,所有的js和css都包括在HTML文档自身中。外部脚本示例包括一个HTML文档(7kb)、一个样式表(59kb)和三个脚本(1kb、11kb和9kb),总计87kb。尽管所需下载的总数据量是相同的,外部脚本示例还是比外部示例快30%到50%。这主要是因为外部示例需要承担多个HTTP请求带来的开销。尽管外部脚本示例可以从样式表和脚本的并行下载中获益,但一个HTTP请求与五个HTTP请求之间的差距致使外部脚本示例更快一些。
尽管结果如此,事实中还是应用外部文件会更合理一些,因为外部文件所带来的收益--------js文件有机会被浏览器缓存起来。HTML文档--------至少是那些包括动态内容的HTML文档--------平日不会被配置为可以进行缓存。当碰到这种情况时(HTML没有被缓存),每次请求HTML文档都要下载外部的js。另一方面,如果js是外部文件,浏览器就可以缓存它们,HTML文档的大小减小,而且不会增长HTTP请求的数量。
关键因素是,与HTML文档请求数量相干的、外部js组件被缓存的频率。这个因素尽管难以量化,但可以通过下面的手段进行衡量:
3.1页面查看
每一个用户发生的页面查看越少,外部js的论据越强势。想象一个普通用户每一个月只拜访你的网站一次。在每次拜访之间,外部js文件很可能从浏览器的缓存中移除。另一方面,如果普通用户能够发生很多的页面查看,浏览器很可能将外部Js文件放在缓存中。应用外部文件提供js带来的收益会随着用户每月的页面查看次数或用户每会话发生的页面查看次数的增长而增长。
3.2空缓存VS完整缓存
在比较外部和外部文件时,晓得用户缓存外部组件的可能性这一点非常重要。我们在Yahoo!进行了丈量,发现天天至少携带完整缓存拜访Yahoo!功能一次的用户占40%到60%。一样的研究表明,具有完整缓存额的页面查看数量占75%到85%。注意第一个统计丈量的是“独一用户”而第二个是“页面查看”。具有完整缓存的页面查看所占的百分比比携带完整缓存的独一用户的百分比高,这是因为很多用户在一次会话中进行了多次页面查看。天天,用户可能只有开始的一次拜访携带的是空缓存,当前的多次后续页面查看都具有完整缓存。如果你的网站的本质上能够为用户带来高完整缓存率,应用外部文件的收益就更大。如果不太可能发生完整缓存,则外部脚本是更好的选择。
3.3组件重用
如果你的网站中的每一个页面都应用了相同的js,应用外部文件可以进步这些组件的重用率。在这种情况下应用外部文件更加具有优势,因为当用户在页面间导航时,js组件已位于浏览器的缓存中了。相反的情况也很容易理解--------如果没有任何两个页面共享相同的js,重用率就会非常低。难的是绝大多数网站不长短黑即白的。这就带来一个单独相干的问题--------当把js打包到外部文件中时,应该把边界划在哪里?
在典范情况下,页面之间的js的重用即不可能100%重叠,也不可能100%无关。在这种旁边情形中,一个极端就是为每一个页面提供一组分离的外部文件。这种方法的缺点在于,每一个页面都强制用户应用另外一组外部组件并发生令响应时光变慢的HTTP请求。这种方法对于普通用户只拜访一个页面和很少进行跨页拜访的网站来说是有意思的。
另一个极端是创建一个单独的、联合了所有js的文件。这只要求用户生成一个HTTP请求,但它增长了用户初次进行页面查看时的下载数据量。在这种情况下,用户浏览页面时要下载的js多于所需的数量。而且,在任何一块独立的脚本转变后,都需要更新这个文件,使所有用户已缓存了的当前版本无效。这种情况对于那些每月会话数量较高、普通用户在一个会话中拜访多个不同页面的网站来说是有意思的。
如果你的网站不符合这两种极端情况,最好的谜底就是折衷。将你的页面分别成几种页面类型,然后为每种类型创建单独的脚本,这比维护一个单独的文件要复杂,但平日比为每一个页面维护不同的脚本要容易,并且对于给定的恣意页面都只需要下载很少的过剩的js。
最后你做出的与js外部文件的边界相干的决议影响着组件的重用水平。如果你可以找到一个平衡点,实现较高的重用性,那么外部文件的论据更强势一些。如果重用度很低,还是外部脚本更有意思些。
在对于外部和外部脚本进行比较分析时,关键点在于与HTML文档请求数量相干的外部js组件被缓存的频率。在此我分析了三种基准(页面查看、空缓存VS完整缓存和组件重用),这有助于你确定最好的选择。对于任何网站来说,准确谜底都依附于这些基准。
大家如果还想更详细的懂得浏览器脚本、css等的一些效率问题,可以看《高性能网站建立指南》,那里头的14条具体的优化原则的确很精辟。
4 将脚本放在底部
4.1脚本带来的问题
下面是一个脚本放在中部的示例
http://stevesouders.com/hpws/js-middle.php
经过编程的脚本下载需要很长时光,因此很容易看到问题--------页面的下半部分要花很长时光才能表现。涌现这一现象是因为脚本阻塞了并行下载。在回想了浏览器如何并行下载当前,我们再回过火处理这一问题。
4.2并行下载
对响应时光影响最大的是页面中组件的数量。当缓存为空,每一个组件都市发生一个HTTP请求,偶然即便缓存是完整的亦是如此。要晓得浏览器会并行地执行HTTP请求,你可能会问,为什么HTTP请求的数量会影响响应时光呢?浏览器不能一次将它们都下载上去吗?
对此的解释要回到HTTP 1.1规范,该规范提议浏览器从每一个主机名并行下载两个组件。很多web页面需要从一个主机名下载所有的组件。查看这些HTTP请求会发现它们是呈阶梯状的,如图所示:
图4.1
如果一个Web页面平均地将其组件分别放在两个主机名下,团体响应时光将可以减少大约一半。HTTP请求的行为看起来会是图4.2所示
图4.2
此处可以并行下载四个组件(每一个主机名两个)。为了对页面加载变快的现象给出可视的效果,其中每一个时光块的横向宽度和图4.1是一样的。
每一个主机名并行下载两个组件的制约只是一个提议。默认情况下浏览器都遵守这一提议,但用户也可以重写该默认设置。但增长并行下载数量并非没有开销的,其好坏取决于你的宽带和CPU速度。
4.3脚本阻塞下载
并行下载组件的优点是很明显的。然而,在一些比较旧的浏览器在下载脚本时并行下载实际上是被禁用的--------即使应用了不同的主机名,浏览器也不会启动其他的下载。其中一个原因是,脚本可能应用document.write来修饰页面内容,因此浏览器会等待,以确保页面能够恰当的布局。(当初的浏览器虽然可以并行下载,但是一样阻塞布局)在下载脚本时浏览器阻塞并行下载的另一个原因是为了保证脚本能够按照准确的顺序执行。如果并行下载多个脚本,就无法保证响应是按照特定顺序到达浏览器的。例如:前面的脚本比页面中之前涌现的脚本更小,它可能首先执行。如果它们之间存在着依附关系,不按照顺序执行就会致使js错误。
如下是一个例子:
http://stevesouders.com/hpws/js-blocking.php
该页面按照顺序包括下列组件
1、来至host1的一个图片
2、来至host2的一个图片
3、来至host1的一个加载需要大约10秒的脚本
4、来至host1的一个图片
5、来至host2的一个图片
4.4最差情况:将脚本放在顶部
至此,脚本对Web页面的影响就清晰了:
1、脚本会阻塞对其前面内容的呈现
2、脚本会阻塞对其前面组件的下载
如果将脚本放在页面顶部--------正如平日情况那样--------页面中的所有东西都位于脚本当前,整个页面的呈现和下载都市被阻塞,直到脚本加载完毕脚本放在顶部的示例:
http:// href="http://stevesouders.com/hpws/js-top.php" target=_blank>stevesouders.com/hpws/js-top.php
由于整个页面的呈现被阻塞,因此致使了白屏现象。逐步呈现对于精良的用户休会来说长短常重要的,但迟缓的脚本下载延迟了用户所期待的反馈。
4.5最佳情况:将脚本放在底部
放置脚本的最好地方时页面的底部。这不会阻挠页面内容的呈现,而且页面中的可是组件可以尽早下载。脚本放在底部的示例:
http:// href="http://stevesouders.com/hpws/js-bottom.php" target=_blank>stevesouders.com/hpws/js-bottom.php
把两个页面--------脚本放在顶部的和脚本放在底部的--------并列放在一起浏览,其对比更为凸起。可以在下面这个示例中看到这一点:
http://stevesouders.com/hpws/move-scripts.php
4.6准确地放置
前面那些示例是应用了大概需要10秒才能下载完的脚本。希望你应用的脚本不需要这么长时光的延迟,但一个脚本很可能消费比预期长的时光,用户的宽带也会影响脚本的响应时光。你的页面中的脚本所发生的影响可能没有这里展示的那么严峻,但仍需要注意。在页面中包括多个脚本也会带来问题。
在很多情况下,很难将脚本移到底部。例如,如果脚本应用document.write向页面中插入了内容,就不能将其移动到页面中靠后的位置。
经常涌现的另外一种提议是应用延迟脚本。Defer属性表明脚本不包括document.write,浏览器得到这一线索便可继续进行呈现。从下面的示例可以看到这一点:
http:// href="http://stevesouders.com/hpws/js-defer.php" target=_blank>stevesouders.com/hpws/js-defer.php
但是不保险,有一些老的浏览器不能识别defer,所以最好还是将脚本放于底部。
5动态加载脚本
详见我之前的博客
6.1异步批量添加外部脚本
很多时候我们由于产品模块的分别,一个页面可能需要加载几个脚本,我们需要斟酌两点:1、脚本之间是否有依附关系,如果存在依附关系即使我们应用script标签是按照顺序的,但是并行下载是一起下载的,如果涌现前面的包先下载完,那么执行脚本时便可能涌现错误;2、斟酌到效率,一般情况下异步加载比同步加载会快一些。为了处理如上问题,我们可以利用之前探讨的知识进行组合
每日一道理
人生好似一条河,既有波澜壮阔,汹涌澎湃,也有清风徐来,水波不兴;人生好似一首歌,既有欢乐的音符,也有悲壮的旋律;人生好似一条船,既有一帆风顺时,也有急流险滩处。愿我们都能勇于经受暴风雨的洗礼,接受生活的挑战和考验!
应用很方便,通过一个方法attachScript可以加载你的恣意多个有序的ja包,并且下载的时候还是并行下载,效率上也还不错,你可以把这个方法单独打包,方便当前应用,不过里头的代码或许需要略微修改一下,有些地方不严谨哦!
6.2同步分类(或模块)动态加载
这里的动态加载是指当用户应用到了某个类或者模块才去加载,并且加载不是用户来控制,而是自动的。
优点:
1、同步加载可以很好的保证脚本的依附关系
2、用时才加载,可以保证基础包尽量小,进步用户休会
缺点:
1、同步加载相对异步加载来说一般偏慢
2、在未宣布的情况下不支持Chrome、Opera
详细的说明请看:js动态加载脚本之实用小技能
6.3异步分类(或模块)动态加载
这里采取回调函数情势的异步加载
优点:
1、异步加载速度快
2、应用回调函数也可以保证脚本的精良依附关系
3、一样时基础包小,进步用户休会
缺点:
1、调试不太方便
2、类被分别为两个部分,分别难度大
说明:大家如果懂得了6.2的同步分模块加载后发现最大的缺点在于未宣布的情况下不支持某些浏览器,并且代码的执行半途会强制被阻挠,当某些代码下载上去后在执行,这样的话所有代码的下载都是呈线性的下载,没有并行,效率会比较低。
不晓得大家有木有注意过百度地图的包和google地图的包是如何实现的,他们实现方法一样:基础包都是大概70到80kb的模样,比较小(所有地图功能包加起来有200到300阁下),第一次拜访地图的时候就比较小,效率快,用户休会好,但是用户在地图下面触发了其他复杂的功能(比如交通换乘)时,你会发现它开始下载一些比较小的包,但这些包是并行下载的。它是如何做到的呢?
其实基础包里头已将所有的API都已定义了,如果没定义,那运行到那一块的时候浏览器确定会报错误,但是所有功能都在基础包里不可能那么小,是因为百度的基础包里头定义的所有的接口(API),但是只有简单的属性是真实可用的,而那些复杂的方法都是空的,也就是这些方法里头只是担任记录是否执行了此方法,那样这些方法必定代码很少,所以基础包就很小,等程序执行一遍后再去下载需要的功能模块,下完后再按照之前记录的信息从新执行一遍,而这些包里头的方法必将覆盖以前的假的方法,当第二次应用的时候就执行的真正的方法了。
下面我们来写一个比较简单的例子看一下:
先看一下测试的页面:
这里点击“按钮”,初始化了一个Button的对象,然后调用了方法getHeight(真的简单方法)和draw(假的复杂方法,第一次调用,只是记录),再次点击“按钮2”,调用方法draw(真的复杂方法,第二次调用,假的已被覆盖了)。
这里Core.js是基础包,代码如下:
这里的SuperMap.Control.Button类为不完整的类,注意构造函数里头有一个回调函数,当整个类加载完后通过init入口从新执行一遍操纵,而里头除了draw是假的以外,其他都是真的。
再来看一下Control.js模块:
看完思路大家就会发现他有自己的缺点:我们调试怎么办,调试的时候获取的对象就不是那么明显了,很不方便;怎么把一个类分别成为两个部分,这一点特别的难,我不是百度的员工,也不清晰他们的标准。不过可以猜想他们这样的有点很明显:你管我代码怎么的,反正用户用起来发现效率很快就行,基础包很小,用到哪块再加载哪块,用户休会特别的好,并行加载,效率快。
没有什么是100%满意的,只要抓住重点就行,百度和google舍弃了调试的便利以及开发的简易(使程序员痛苦),但是获得了广大用户的精良评价,这就是他们的目的。
这里的三点实用方法希望能给大家给予必定的帮助吧!
文章结束给大家分享下程序员的一些笑话语录: 姿势要丰富,经常上百度!
这段时光工作工作上不是很紧,零星的在研究浏览器的一些东西,刚好这个月又一次轮到我做沙龙讲座了,想好了许久,就来一次js脚本加载的总结吧!这一块应该对于很多做项目的朋友来会有所帮助吧!
1、js来源
总所周知网页最开始的形态是静态的(也就是所谓的静态网页),那时候的网页主要用于浏览资料信息,可是随着用户需求的增长,用户希望在页面上做一些交互操纵,比如页面需要验证才能拜访、页面输入的一些数据希望下次还可以拜访等等,js的涌现满意了人们的需求。也就涌现了所谓的动态网页。
虽然js给人们带来了很好的交互性,但是起初人们乱用脚本,只是网页页面代码凌乱不堪,当css的涌现处理了这一问题:
晓得如今,我们的网页也是分为三部分:HTML(主要放置界面元素)、CSS(主要担任界面布局)和JS(主要担任界面交互)。
大家都晓得这三部分,但是有很多人不注意一些细节,致使做出的网页拜访时效率低下。在这里我将和大家探讨一下关于js的一些知识,希望给大家带来必定帮助吧!
2、三种应用脚本方法
2.1外部引用JavaScript
2.11通过HTML的script标签加载JavaScript代码
如:
<head> <script type="text/javascript"> document.write("Hello World !"); </script> </head>
2.12通过注释隐藏JavaScript代码
如:
<head> <script type="text/javascript"> <!-- document.write("Hello World !"); //--> </script> </head>
<!-- ... //-->当浏览器不支持JavaScript时,屏蔽JavaScript代码。这个代码是骇客技术,<!-- ... -->于HTML注释,// 是JavaScript注释。当浏览器支持JavaScript时//代码生效,因此HTML的注释没有效果;当浏览器不支JavaScript时,//代码无效,因此屏蔽了<!--
... -->之间的JavaScript代码。当初这种隐藏JavaScript代码的方法可以疏忽,因为没有浏览器不支持JavaScript,除了部分用户手动制止浏览器的JavaScript功能,但是这种情况很少发生。
2.1.3应用noscript标签为用户提供更好的休会
如:
<body> <script type="text/javascript"> document.write("Hello World !"); </script> <noscript> <p>如果您想查看此网页,则必须启用JavaScript。 然而,JavaScript 仿佛被禁用,要么就是您的 浏览器不支持 JavaScript。请更改您的浏览器 选项以启用 JavaScript,然后刷新。 </p> </noscript> </body>
通过JavaScript注释的方法可以隐藏JavaScript代码,通过noscript标签可认为用户提供更好的休会(提示用户你的浏览器不支持JavaScript)。
2.2外部引用JavaScript
应用<script>标签的src属性来加载js脚本。平日JavaScript文件可以应用script标签加载到网页的任何一个地方,但是标准的方法是加载在head标签内。为避免网页加载迟缓,也可以把非关键的JavaScript放到网页底部。
<script type="text/javascript" src=“SuperMap.js"></script>
这里有几点好处:
1)避免应用<!-- ... //-->,骇客技术。
2)统必定义JavaScript代码,方便查看,方便维护。
3)使代码更安全,可以压缩,加密单个JavaScript文件。
4)浏览器可以缓存JavaScript文件,减少宽带应用。
2.3内联引用JavaScript
内联引用是通过HTML标签中的事件属性实现的。
<input type="button" value="点我" onclick="alert('你点击了一个按钮');">
下面示例将调用input标签的onclick属性,弹出一个提示框。
3内外脚本的比较
内联脚本方法应用场景很少,几乎没什么优势。
外部脚本示例:http://stevesouders.com/hpws/inlined.php
外部脚本示例:http://stevesouders.com/hpws/external.php
外部脚本示例只有一个HTML文档,其大小为87kb,所有的js和css都包括在HTML文档自身中。外部脚本示例包括一个HTML文档(7kb)、一个样式表(59kb)和三个脚本(1kb、11kb和9kb),总计87kb。尽管所需下载的总数据量是相同的,外部脚本示例还是比外部示例快30%到50%。这主要是因为外部示例需要承担多个HTTP请求带来的开销。尽管外部脚本示例可以从样式表和脚本的并行下载中获益,但一个HTTP请求与五个HTTP请求之间的差距致使外部脚本示例更快一些。
尽管结果如此,事实中还是应用外部文件会更合理一些,因为外部文件所带来的收益--------js文件有机会被浏览器缓存起来。HTML文档--------至少是那些包括动态内容的HTML文档--------平日不会被配置为可以进行缓存。当碰到这种情况时(HTML没有被缓存),每次请求HTML文档都要下载外部的js。另一方面,如果js是外部文件,浏览器就可以缓存它们,HTML文档的大小减小,而且不会增长HTTP请求的数量。
关键因素是,与HTML文档请求数量相干的、外部js组件被缓存的频率。这个因素尽管难以量化,但可以通过下面的手段进行衡量:
3.1页面查看
每一个用户发生的页面查看越少,外部js的论据越强势。想象一个普通用户每一个月只拜访你的网站一次。在每次拜访之间,外部js文件很可能从浏览器的缓存中移除。另一方面,如果普通用户能够发生很多的页面查看,浏览器很可能将外部Js文件放在缓存中。应用外部文件提供js带来的收益会随着用户每月的页面查看次数或用户每会话发生的页面查看次数的增长而增长。
3.2空缓存VS完整缓存
在比较外部和外部文件时,晓得用户缓存外部组件的可能性这一点非常重要。我们在Yahoo!进行了丈量,发现天天至少携带完整缓存拜访Yahoo!功能一次的用户占40%到60%。一样的研究表明,具有完整缓存额的页面查看数量占75%到85%。注意第一个统计丈量的是“独一用户”而第二个是“页面查看”。具有完整缓存的页面查看所占的百分比比携带完整缓存的独一用户的百分比高,这是因为很多用户在一次会话中进行了多次页面查看。天天,用户可能只有开始的一次拜访携带的是空缓存,当前的多次后续页面查看都具有完整缓存。如果你的网站的本质上能够为用户带来高完整缓存率,应用外部文件的收益就更大。如果不太可能发生完整缓存,则外部脚本是更好的选择。
3.3组件重用
如果你的网站中的每一个页面都应用了相同的js,应用外部文件可以进步这些组件的重用率。在这种情况下应用外部文件更加具有优势,因为当用户在页面间导航时,js组件已位于浏览器的缓存中了。相反的情况也很容易理解--------如果没有任何两个页面共享相同的js,重用率就会非常低。难的是绝大多数网站不长短黑即白的。这就带来一个单独相干的问题--------当把js打包到外部文件中时,应该把边界划在哪里?
在典范情况下,页面之间的js的重用即不可能100%重叠,也不可能100%无关。在这种旁边情形中,一个极端就是为每一个页面提供一组分离的外部文件。这种方法的缺点在于,每一个页面都强制用户应用另外一组外部组件并发生令响应时光变慢的HTTP请求。这种方法对于普通用户只拜访一个页面和很少进行跨页拜访的网站来说是有意思的。
另一个极端是创建一个单独的、联合了所有js的文件。这只要求用户生成一个HTTP请求,但它增长了用户初次进行页面查看时的下载数据量。在这种情况下,用户浏览页面时要下载的js多于所需的数量。而且,在任何一块独立的脚本转变后,都需要更新这个文件,使所有用户已缓存了的当前版本无效。这种情况对于那些每月会话数量较高、普通用户在一个会话中拜访多个不同页面的网站来说是有意思的。
如果你的网站不符合这两种极端情况,最好的谜底就是折衷。将你的页面分别成几种页面类型,然后为每种类型创建单独的脚本,这比维护一个单独的文件要复杂,但平日比为每一个页面维护不同的脚本要容易,并且对于给定的恣意页面都只需要下载很少的过剩的js。
最后你做出的与js外部文件的边界相干的决议影响着组件的重用水平。如果你可以找到一个平衡点,实现较高的重用性,那么外部文件的论据更强势一些。如果重用度很低,还是外部脚本更有意思些。
在对于外部和外部脚本进行比较分析时,关键点在于与HTML文档请求数量相干的外部js组件被缓存的频率。在此我分析了三种基准(页面查看、空缓存VS完整缓存和组件重用),这有助于你确定最好的选择。对于任何网站来说,准确谜底都依附于这些基准。
大家如果还想更详细的懂得浏览器脚本、css等的一些效率问题,可以看《高性能网站建立指南》,那里头的14条具体的优化原则的确很精辟。
4 将脚本放在底部
4.1脚本带来的问题
下面是一个脚本放在中部的示例
http://stevesouders.com/hpws/js-middle.php
经过编程的脚本下载需要很长时光,因此很容易看到问题--------页面的下半部分要花很长时光才能表现。涌现这一现象是因为脚本阻塞了并行下载。在回想了浏览器如何并行下载当前,我们再回过火处理这一问题。
4.2并行下载
对响应时光影响最大的是页面中组件的数量。当缓存为空,每一个组件都市发生一个HTTP请求,偶然即便缓存是完整的亦是如此。要晓得浏览器会并行地执行HTTP请求,你可能会问,为什么HTTP请求的数量会影响响应时光呢?浏览器不能一次将它们都下载上去吗?
对此的解释要回到HTTP 1.1规范,该规范提议浏览器从每一个主机名并行下载两个组件。很多web页面需要从一个主机名下载所有的组件。查看这些HTTP请求会发现它们是呈阶梯状的,如图所示:
图4.1
如果一个Web页面平均地将其组件分别放在两个主机名下,团体响应时光将可以减少大约一半。HTTP请求的行为看起来会是图4.2所示
图4.2
此处可以并行下载四个组件(每一个主机名两个)。为了对页面加载变快的现象给出可视的效果,其中每一个时光块的横向宽度和图4.1是一样的。
每一个主机名并行下载两个组件的制约只是一个提议。默认情况下浏览器都遵守这一提议,但用户也可以重写该默认设置。但增长并行下载数量并非没有开销的,其好坏取决于你的宽带和CPU速度。
4.3脚本阻塞下载
并行下载组件的优点是很明显的。然而,在一些比较旧的浏览器在下载脚本时并行下载实际上是被禁用的--------即使应用了不同的主机名,浏览器也不会启动其他的下载。其中一个原因是,脚本可能应用document.write来修饰页面内容,因此浏览器会等待,以确保页面能够恰当的布局。(当初的浏览器虽然可以并行下载,但是一样阻塞布局)在下载脚本时浏览器阻塞并行下载的另一个原因是为了保证脚本能够按照准确的顺序执行。如果并行下载多个脚本,就无法保证响应是按照特定顺序到达浏览器的。例如:前面的脚本比页面中之前涌现的脚本更小,它可能首先执行。如果它们之间存在着依附关系,不按照顺序执行就会致使js错误。
如下是一个例子:
http://stevesouders.com/hpws/js-blocking.php
该页面按照顺序包括下列组件
1、来至host1的一个图片
2、来至host2的一个图片
3、来至host1的一个加载需要大约10秒的脚本
4、来至host1的一个图片
5、来至host2的一个图片
4.4最差情况:将脚本放在顶部
至此,脚本对Web页面的影响就清晰了:
1、脚本会阻塞对其前面内容的呈现
2、脚本会阻塞对其前面组件的下载
如果将脚本放在页面顶部--------正如平日情况那样--------页面中的所有东西都位于脚本当前,整个页面的呈现和下载都市被阻塞,直到脚本加载完毕脚本放在顶部的示例:
http:// href="http://stevesouders.com/hpws/js-top.php" target=_blank>stevesouders.com/hpws/js-top.php
由于整个页面的呈现被阻塞,因此致使了白屏现象。逐步呈现对于精良的用户休会来说长短常重要的,但迟缓的脚本下载延迟了用户所期待的反馈。
4.5最佳情况:将脚本放在底部
放置脚本的最好地方时页面的底部。这不会阻挠页面内容的呈现,而且页面中的可是组件可以尽早下载。脚本放在底部的示例:
http:// href="http://stevesouders.com/hpws/js-bottom.php" target=_blank>stevesouders.com/hpws/js-bottom.php
把两个页面--------脚本放在顶部的和脚本放在底部的--------并列放在一起浏览,其对比更为凸起。可以在下面这个示例中看到这一点:
http://stevesouders.com/hpws/move-scripts.php
4.6准确地放置
前面那些示例是应用了大概需要10秒才能下载完的脚本。希望你应用的脚本不需要这么长时光的延迟,但一个脚本很可能消费比预期长的时光,用户的宽带也会影响脚本的响应时光。你的页面中的脚本所发生的影响可能没有这里展示的那么严峻,但仍需要注意。在页面中包括多个脚本也会带来问题。
在很多情况下,很难将脚本移到底部。例如,如果脚本应用document.write向页面中插入了内容,就不能将其移动到页面中靠后的位置。
经常涌现的另外一种提议是应用延迟脚本。Defer属性表明脚本不包括document.write,浏览器得到这一线索便可继续进行呈现。从下面的示例可以看到这一点:
http:// href="http://stevesouders.com/hpws/js-defer.php" target=_blank>stevesouders.com/hpws/js-defer.php
但是不保险,有一些老的浏览器不能识别defer,所以最好还是将脚本放于底部。
5动态加载脚本
详见我之前的博客
js动态加载脚本
6三种实用方法6.1异步批量添加外部脚本
很多时候我们由于产品模块的分别,一个页面可能需要加载几个脚本,我们需要斟酌两点:1、脚本之间是否有依附关系,如果存在依附关系即使我们应用script标签是按照顺序的,但是并行下载是一起下载的,如果涌现前面的包先下载完,那么执行脚本时便可能涌现错误;2、斟酌到效率,一般情况下异步加载比同步加载会快一些。为了处理如上问题,我们可以利用之前探讨的知识进行组合
<html> <head> <title></title> <script type="text/javascript"> function init() { //这里第一个参数是一个数组,可以恣意多个,加载顺序按照数组的顺序进行保证 //第二个参数是回调函数,当所有包都确认加载完毕后需要执行的脚本 //第三个参数是script的标签,这个参数可以省略,没有本质意思 attachScript(["http://www.cnblogs.com/5/loadJS.js","http://www.cnblogs.com/5/package.js"],operation,"yy")(); } function operation() { //可以运行,表现“成功加载” functionOne(); } //异步批量加载脚本,并且根据数组urlArray中的url顺序来加载 function attachScript(urlArray, callback, id) { if(urlArray && ((typeof urlArray) == "object")) { if(urlArray.constructor == Array) { if(urlArray.length>1) { var array = urlArray.splice(0,1); return function(){ var dataScript = document.createElement('script'); dataScript.type = 'text/javascript'; if(dataScript.readyState) { //IE dataScript.onreadystatechange = function() { if(dataScript.readyState == 'complete'|| dataScript.readyState == 'loaded'){ dataScript.onreadystatechange = null; attachScript(urlArray,callback,id)(); } } } else { //standers dataScript.onload = function() { attachScript(urlArray,callback,id)(); } } dataScript.src = array[0]; dataScript.id = id; document.body.appendChild(dataScript); } } else if(urlArray.length == 1) { return function(){ var dataScript = document.createElement('script'); dataScript.type = 'text/javascript'; if(dataScript.readyState) { //IE dataScript.onreadystatechange = function() { if(dataScript.readyState == 'complete'|| dataScript.readyState == 'loaded'){ dataScript.onreadystatechange = null; callback(); } } } else { //standers dataScript.onload = function() { callback(); } } dataScript.src = urlArray[0]; dataScript.id = id; document.body.appendChild(dataScript); } } } } } </script> </head> <body> <input type="button" value="测试按钮" onclick="init()"/> </body> </html>
每日一道理
人生好似一条河,既有波澜壮阔,汹涌澎湃,也有清风徐来,水波不兴;人生好似一首歌,既有欢乐的音符,也有悲壮的旋律;人生好似一条船,既有一帆风顺时,也有急流险滩处。愿我们都能勇于经受暴风雨的洗礼,接受生活的挑战和考验!
应用很方便,通过一个方法attachScript可以加载你的恣意多个有序的ja包,并且下载的时候还是并行下载,效率上也还不错,你可以把这个方法单独打包,方便当前应用,不过里头的代码或许需要略微修改一下,有些地方不严谨哦!
6.2同步分类(或模块)动态加载
这里的动态加载是指当用户应用到了某个类或者模块才去加载,并且加载不是用户来控制,而是自动的。
优点:
1、同步加载可以很好的保证脚本的依附关系
2、用时才加载,可以保证基础包尽量小,进步用户休会
缺点:
1、同步加载相对异步加载来说一般偏慢
2、在未宣布的情况下不支持Chrome、Opera
详细的说明请看:js动态加载脚本之实用小技能
6.3异步分类(或模块)动态加载
这里采取回调函数情势的异步加载
优点:
1、异步加载速度快
2、应用回调函数也可以保证脚本的精良依附关系
3、一样时基础包小,进步用户休会
缺点:
1、调试不太方便
2、类被分别为两个部分,分别难度大
说明:大家如果懂得了6.2的同步分模块加载后发现最大的缺点在于未宣布的情况下不支持某些浏览器,并且代码的执行半途会强制被阻挠,当某些代码下载上去后在执行,这样的话所有代码的下载都是呈线性的下载,没有并行,效率会比较低。
不晓得大家有木有注意过百度地图的包和google地图的包是如何实现的,他们实现方法一样:基础包都是大概70到80kb的模样,比较小(所有地图功能包加起来有200到300阁下),第一次拜访地图的时候就比较小,效率快,用户休会好,但是用户在地图下面触发了其他复杂的功能(比如交通换乘)时,你会发现它开始下载一些比较小的包,但这些包是并行下载的。它是如何做到的呢?
其实基础包里头已将所有的API都已定义了,如果没定义,那运行到那一块的时候浏览器确定会报错误,但是所有功能都在基础包里不可能那么小,是因为百度的基础包里头定义的所有的接口(API),但是只有简单的属性是真实可用的,而那些复杂的方法都是空的,也就是这些方法里头只是担任记录是否执行了此方法,那样这些方法必定代码很少,所以基础包就很小,等程序执行一遍后再去下载需要的功能模块,下完后再按照之前记录的信息从新执行一遍,而这些包里头的方法必将覆盖以前的假的方法,当第二次应用的时候就执行的真正的方法了。
下面我们来写一个比较简单的例子看一下:
先看一下测试的页面:
<html> <head> <title></title> <script type="text/javascript" src="Core.js"></script> <script type="text/javascript"> var button; function init1() { button =new SuperMap.Control.Button(20,10); var height = button.getHeight(); button.draw(); } function init2() { button.draw(); } </script> </head> <body> <input type="button" onclick="init1()" value="按钮" /> <input type="button" onclick="init2()" value="按钮2" /> </body> </html>
这里点击“按钮”,初始化了一个Button的对象,然后调用了方法getHeight(真的简单方法)和draw(假的复杂方法,第一次调用,只是记录),再次点击“按钮2”,调用方法draw(真的复杂方法,第二次调用,假的已被覆盖了)。
这里Core.js是基础包,代码如下:
//一下为框架代码,大家不需要懂得 window.SuperMap = { VERSION_NUMBER: "Release 6.1.3", _getScriptLocation: (function() { //SuperMap-6.1.1-8828 var r = new RegExp("(^|(.*?\\/))(SuperMap(-(\\d{1}\.)*\\d{1}-\\d{4,})?\.js)(\\?|$)"), s = document.getElementsByTagName('script'), src, m, l = ""; for(var i=0, len=s.length; i<len; i++) { src = s[i].getAttribute('src'); if(src) { var m = src.match(r); if(m) { l = m[1]; break; } } } return (function() { return l; }); })() }; SuperMap.Control = SuperMap.Control || {}; SuperMap.Util = SuperMap.Util || {}; SuperMap.Class = function() { var len = arguments.length; var P = arguments[0]; var F = arguments[len-1]; var C = typeof F.initialize == "function" ? F.initialize : function(){ P.prototype.initialize.apply(this, arguments); }; if (len > 1) { var newArgs = [C, P].concat( Array.prototype.slice.call(arguments).slice(1, len-1), F); SuperMap.inherit.apply(null, newArgs); } else { C.prototype = F; } return C; }; SuperMap.inherit = function(C, P) { var F = function() {}; F.prototype = P.prototype; C.prototype = new F; var i, l, o; for(i=2, l=arguments.length; i<l; i++) { o = arguments[i]; if(typeof o === "function") { o = o.prototype; } SuperMap.Util.extend(C.prototype, o); } }; SuperMap.Util = SuperMap.Util || {}; SuperMap.Util.extend = function(destination, source) { destination = destination || {}; if (source) { for (var property in source) { var value = source[property]; if (value !== undefined) { destination[property] = value; } } var sourceIsEvt = typeof window.Event == "function" && source instanceof window.Event; if (!sourceIsEvt && source.hasOwnProperty && source.hasOwnProperty("toString")) { destination.toString = source.toString; } } return destination; }; SuperMap.Util.copy = function(des, soc) { des = des || {}; var v; if(soc) { for(var p in des) { v = soc[p]; if(typeof v !== 'undefined') { des[p] = v; } } } }; SuperMap.Util.reset = function(obj) { obj = obj || {}; for(var p in obj) { if(obj.hasOwnProperty(p)) { if(typeof obj[p] === "object" && obj[p] instanceof Array) { for(var i in obj[p]) { if(obj[p][i].destroy) { obj[p][i].destroy(); } } obj[p].length = 0; } else if(typeof obj[p] === "object" && obj[p] instanceof Object) { if(obj[p].destroy) { obj[p].destroy(); } } obj[p] = null; } } }; //以下为核心代码 //加载脚本的方法 SuperMap.Util.loadJs = function(urlArray, callback, id) { if(urlArray && ((typeof urlArray) == "object")) { if(urlArray.constructor == Array) { if(urlArray.length>1) { var array = urlArray.splice(0,1); return function(){ var dataScript = document.createElement('script'); dataScript.type = 'text/javascript'; if(dataScript.readyState) { //IE dataScript.onreadystatechange = function() { if(dataScript.readyState == 'complete'|| dataScript.readyState == 'loaded'){ dataScript.onreadystatechange = null; SuperMap.Util.loadJs(urlArray,callback,id)(); } } } else { //standers dataScript.onload = function() { SuperMap.Util.loadJs(urlArray,callback,id)(); } } dataScript.src = array[0]; dataScript.id = id; document.body.appendChild(dataScript); } } else if(urlArray.length == 1) { return function(){ var dataScript = document.createElement('script'); dataScript.type = 'text/javascript'; if(dataScript.readyState) { //IE dataScript.onreadystatechange = function() { if(dataScript.readyState == 'complete'|| dataScript.readyState == 'loaded'){ dataScript.onreadystatechange = null; callback(); } } } else { //standers dataScript.onload = function() { callback(); } } dataScript.src = urlArray[0]; dataScript.id = id; document.body.appendChild(dataScript); } } } } } //用于记录模块是否加载 SuperMap.Util.IsControl = false; //按照模块名称来加载脚本 SuperMap.Util.load = function(backName,callbackFunction){ if(backName == "Control") { if(SuperMap.Util.IsControl == false) { SuperMap.Util.loadJs(["Control.js"],callbackFunction,546756)(); } SuperMap.Util.IsControl == true; } //其他模块 else if(...) { ... } //.... } //用于测试的类 Control模块 SuperMap.Control.Button = SuperMap.Class({ w: 0.0, h: 0.0, initialize: function(w, h) { this.w = parseFloat(w); this.h = parseFloat(h); this.flow = []; //需要注册一个回调函数 var c = this; SuperMap.Util.load("Control",function(){ //等加载完脚本后从新执行一遍 c.init(); }); }, getWidth:function() { return this.w; }, setWidth:function(value) { this.w = value; }, getHeight:function() { return this.h; }, setHeight:function(value) { this.h = value; }, getArea:function() { return this.w * this.h; }, clone:function() { return new SuperMap.Control.Button(this.w, this.h); }, //假的方法 draw:function(){ this.flow.push({method: "draw", arguments: null}); }, CLASS_NAME: "SuperMap.Control.Button" });
这里的SuperMap.Control.Button类为不完整的类,注意构造函数里头有一个回调函数,当整个类加载完后通过init入口从新执行一遍操纵,而里头除了draw是假的以外,其他都是真的。
再来看一下Control.js模块:
var Button = SuperMap.Control.Button; //必须有的入口方法 Button.prototype.init = function() { for(var i = 0;i<this.flow.length;i++) { //挨个执行 this[this.flow[i].method](this.flow[i].arguments); } delete this.flow; } //比较复杂的方法 Button.prototype.draw = function() { //.... //alert("绘制完毕"); }
看完思路大家就会发现他有自己的缺点:我们调试怎么办,调试的时候获取的对象就不是那么明显了,很不方便;怎么把一个类分别成为两个部分,这一点特别的难,我不是百度的员工,也不清晰他们的标准。不过可以猜想他们这样的有点很明显:你管我代码怎么的,反正用户用起来发现效率很快就行,基础包很小,用到哪块再加载哪块,用户休会特别的好,并行加载,效率快。
没有什么是100%满意的,只要抓住重点就行,百度和google舍弃了调试的便利以及开发的简易(使程序员痛苦),但是获得了广大用户的精良评价,这就是他们的目的。
这里的三点实用方法希望能给大家给予必定的帮助吧!
文章结束给大家分享下程序员的一些笑话语录: 姿势要丰富,经常上百度!
相关文章推荐
- js页面加载完成后加载JS脚本方法
- js脚本加载总结
- Ajax中主页加载分页面后,分页面js脚本不执行的解决办法
- 执行ajax加载页面中的js总结
- Ajax中主页加载分页面后,分页面js脚本不执行的解决办法
- 每日总结:每个 GROUP BY 表达式必须至少包含一个不是外部引用的列、加载页面时调用Js方法、调用Js文件中的方法
- JS实用脚本--页面加载完毕后执行函数
- JS脚本文件的位置对页面加载性能影响以及无阻塞脚本(javascript)模式
- JS脚本文件的位置对页面加载性能影响以及无阻塞脚本(javascript)模式
- js网页顶部线性页面加载进度条,jquery头部线性进度条总结
- 利用JS脚本把另一个页面加载到当前页面
- js脚本加载总结
- js脚本加载总结
- js页面初始化加载的总结
- Nodejs+Express中页面控制器及脚本自动加载设计
- 通过二次加载脚本,解决Ajax加载的页面中JS脚本不执行问题
- js动态加载控件jsp页面
- js跳转页面方法总结
- 页面加入_JS,CSS使用,#include添加文件,缓存页面,页面间传递汉字,IsPostBack控制页面的加载,自定义错语页面
- 动态加载js文件以支持跨域脚本