Javascript在浏览器性能中,这可能是所有开发者比较关注的问题,因为Javascript有阻塞的特征,也就是当Javascript运行的时候,浏览器不会处理其他的任务。但是浏览器不可能只运行一个任务,但是同一时间又只能执行单个任务。
不管Javascript代码是内联的还是包含在一个外部文件中的,页面的下载和解析就必须等待脚本完成,才能继续向下执行,这样的原因是因为脚本的执行可能会重新渲染页面UI。我们典型的脚本函数是这样的。如:
Script Example
当浏览器遇到一个<script>标签时,正如上面的HTML页面那样,没办法知道Javascript是不是在div标签中添加或者删除内容,这样浏览器就停止,运行完当前的脚本,然后再继续执行下面的内容。当然使用外链也是这样的过程,遇到src外链Javascript代码,浏览器也是首先加载这个外部Javascript文件,然后解析运行此Javascript代码,至于什么时候执行,完全要下载此文件需要多久的时间。
1、脚本的位置
从HTML4开始,明确之处,一个<script>标签可以放在HTML文档的<head>和<body>标签中,当然不仅限制是链接一个<script>标签,我们更多的做法是使用<link>标签加载外部的Javascript文件,当然了,其中也包含css文件。如:
这是Javascript文件引入的例子。但是这样的写法理论上是没有任何问题的,但是这里就存在了性能和体验的问题。上面的代码加载了3个外部文件,每个文件在加载的过程中阻塞了页面的解析,浏览器只有等待它们下载并运行了Javascript代码之后,页面才能继续,这我们在上面已经提到过了。最致命的问题就是,把Javascript文件放在顶部,在加载Javascript文件比较慢的时候会出现空白页,以至于用户看不到页面,更不要说交互网页,推荐的办法就是,把所有的Javascript文件,包括外链的文件挡在标签底部位置,减少对整个页面加载的影响。这里就不列出例子了。
2、延迟脚本
这个时候就<script>标签出现了一个属性:defer。一个带有defere属性的<script>标签可以防止在文档的任何位置,对应的Javascript文件将在<script>被解析时启动下载,但是代码不会执行,知道DOM加载完成。当一个带有defer的Javascript文件被下载时,他不会阻塞浏览器的其他处理过程,所以这些文件可以与其他页面的其他资源一起并行下载。如:
Script Defer Example ## 标题文字 ##
如果浏览器不支持defer属性,上面的代码的运行顺序是: 延迟-》当前-》加载完成。如果浏览器支持defer属性,那么运行顺序是:当前-》延迟-》加载完成。应该很直观的就能看出区别。目前defer标签目前已被所有主流浏览器支持
另外这不能不提的还有一个属性:async。用于加载异步脚本,async和defer的相同点是采用并行下载,在下载的过程中都是不会产生阻塞。区别在于执行时机,async是加载完成后自动执行,,而defer需要等待页面完成后执行。
3、动态脚本元素
DOM允许使用Javascript动态创建HTML支持的全部内容。如:
var script = document.createElement ("script");script.type = "text/javascript";script.src = "file1.js"; document.getElementsByTagName_r("head")[0].appendChild(script);
上面新的<script>元素file.js源文件,此文件被添加到页面之后立刻开始下载。最重要的一点是这部分代码的加载和运行不会阻塞其他页面处理过程。
这里有一个封装方法,可以实现浏览器兼容模式:
function loadScript(url, callback){ var script = document.createElement ("script") script.type = "text/javascript"; if (script.readyState){ //IE script.onreadystatechange = function(){ if (script.readyState == "loaded" || script.readyState == "complete"){ script.onreadystatechange = null; callback(); } }; } else { //Others script.onload = function(){ callback(); }; } script.src = url; document.getElementsByTagName_r("head")[0].appendChild(script); }
此方法接受两个参数:url,和一个当Javascript接受完成时触发的回调函数。最后设置src属性,再将<script>元素添加到页面。使用方法为:
loadScript("file1.js", function(){ alert("文件加载完成!");});
4、XHR脚本注入
还有一个非阻塞方式活的脚本的方法是使用XMLHttpRequest(XHR)对象加载Javascript脚本,原理就是创建一个XHR对象,然后下载Javascript文件,再用一个动态的<script>元素将Javascript代码注入页面。如:
var xhr = new XMLHttpRequest(); xhr.open("get", "file1.js", true); xhr.onreadystatechange = function(){ if (xhr.readyState == 4){ if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304){ var script = document.createElement ("script"); script.type = "text/javascript"; script.text = xhr.responseText; document.body.appendChild(script); } }}; xhr.send(null);
此代码向服务器发送一个获取file.js文件的请求,这个请求是GET模式,onreadystatechange事件处理函数检查readyState是不是4,然后检查HTTP状态码是不是有效的2XX有效的回应,或者是304的缓存相应。如果收到有效相应,那么就创建一个新的<script>元素,然后文本属性设置为从服务接收到的respinseText字符串。这样做实际上会chaungjian一个带有内联代码的<sript>元素。一旦新的<script>元素被添加到文档,代码将会被执行。
这样的好处是,可以下载但不是立即执行Javascript代码,还有一个好处是兼容性好。
缺点就是不能跨域使用。5、LazyLoad 类库
Yahoo! Search的工程师创建了一个更为通用的LazyLoad库(参见:)LazyLoad是一个更加强大的loadScript()函数,LazyLoad压缩后体积很小,用法:
LazyLoad也可以同时下载多个Javascript文件,并保证它们在所有浏览器上都能以正确的顺序远行,要加载多个Javascript文件,只需要在执行LazyLoad()函数时传递一个数组即可,如:
当然还有其他类库,比如 LABjs 这里就不一一介绍了。
总结
将所有的<script>标签放置在页面底部,紧靠body标签的上方,些方法可以保证页面在脚本运行之前完成解析。最好无论Javascript文件是以什么样的方式加载的。
讲脚本打包,尽量合并页面的Javascript文件,文件越少,页面的加载速度就会越快,无论是内联的还是外链的Javascript文件。同步博客地址:
微博: