参考 高性能javascript
当浏览器解析到<script>标签的时候,无论这个js代码是内嵌的还是外链的,都会导致页面的下载和渲染阻塞,当js代码下载并执行完毕后,继续页面的下载和渲染(因为脚本的执行过程可能会存在修改页面内容的情况,所以会阻塞页面的下载和渲染)
我们可以将<script>放在页面的<head>或者<body>,首先我们先将<script>标签放在<head>中
<!DOCTYPE html> <head> <meta charset="UTF-8" /> <script type="text/javascript" src="a.js"></script> <script type="text/javascript" src="b.js"></script> </head> <body> <p>hello</p> </body>
当以上面的方式放置并加载js代码时,由于js代码会阻塞页面的渲染和其他的元素的下载(允许js并行下载),会导致延迟,变现为页面的空白并且用户无法与页面进行交互
因此推荐将<script>标签放在页面的<body>标签的底部
<!DOCTYPE html> <head> <meta charset="UTF-8" /> </head> <body> <p>hello</p> <script type="text/javascript" src="a.js"></script> <script type="text/javascript" src="b.js"></script> </body>
这样当下载和执行js代码的时候页面的大部分内容已经下载完成并显示给了用户。
由于<script>标签会阻塞页面的渲染(即使将<script>标签放在页面的底部,会阻塞未下载完成的其他非js元素)而且还会因为脚本的执行导致一定的延迟,所以最小化延迟时间会改善页面的总体性能
当处理外链的javascript文件时,http请求会带来相应开销,因此我们可以减少外链脚本文件的数量,例如可以通过雅虎的合并处理器,可以通过一个url加载多个js文件(注意这些文件在服务器是独立存在的,如果简单的将两个js文件在服务器中合并,在加载的时候只加载合并的文件,这样不仅导致文件的体积变大造成更大的延迟也缺少复用的可能)
无阻塞脚本
通过减少js文件大小并限制http请求数可以提高一定的页面性能,当时当页面越来越复杂的时候,精简源代码并不是总可行,因此提出了无阻塞脚本即在页面加载完成后才加载js代码也就是在window对象的onload事件触发后在下载脚本
1)延迟脚本 defer
这并不是一个理想的跨浏览器解决方案,当我们为<script>标签设置defer属性时,就指明了该元素所含的脚本不会修改DOM,因此代码能安全的延迟执行,但是不支持defer属性的浏览器的时候,<script>标签会以默认的方式处理(阻塞),带有defer属性的<script>可以放在页面的任何位置,在解析到这个<script>标签时候开始现在但不会执行直到DOM加载完成(onload事件被触发前执行),带有defer属性的<script>标签文件下载时,不会阻塞浏览器的其他进程,可以与页面的其他资源并行下载
2)动态脚本元素
这种方法是用标准的DOM方法创建新的<script>元素
var script = document.createElement("script"); script.type = "text/javascript"; script.src = "a.js"; document.getElementsByTagName("head")[0].appendChild(script);
以这种方式创建并且加载js文件,文件在该元素被添加到页面的时候开始下载,并且在下载和执行的过程中不会阻塞页面的其他进程
但是当这种方式加载js代码,提供的页面其他脚本调用的借口时(提供函数供其他部分使用),就必须确保脚本下载完成并准备就绪
在非IE浏览器中 <script>标签在接收完成时触发一个load事件,可以通过侦听此事件来获得脚本加载完成时的状态
在IE浏览器中 会触发readystatechange事件,主要同时检测两个状态 loaded complete 只要满足其中的一个触发,就删除事件处理器(确保事件不会处理两次)
下面就是相应的方法实现(跨浏览器实现)
<script type="text/javascript"> 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 {//非IE script.onload = function() { callback(); } } script.src = url; document.getElementsByTagName(‘head‘)[0].appendChild(script); } </script>