- Helloweba
- 首页
- html/CSS
- Javascript/jQuery
- PHP/MySQL
- HTML5/移动WEB应用
- 前端收录
- 加快JavaScript加载和执行效率
- 时间:2017-01-31 21:54
- document.write("Today is " + (new Date()).toDateString());
- 当浏览器遇到 标签添加内容,或引入其他元素,或甚至移除该标签。因此,这时浏览器会停止处理页面,先执行 JavaScript代码,然后再继续解析和渲染页面。同样的情况也发生在使用 src 属性加载 JavaScript的过程中,浏览器必须先花时间下载外链文件中的代码,然后解析并执行它。在这个过程中,页面渲染和用户交互完全被阻塞了。
- 脚本位置
- HTML 4 规范指出 标签用来加载外链的 CSS 文件或者其他页面信息。
- 低效率脚本位置示例
- Hello world!
- 然而这种常规的做法却隐藏着严重的性能问题。在清单 2 的示例中,当浏览器解析到 标签无法被加载,那么页面自然就无法渲染了。因此在该 JavaScript 代码完全执行完之前,页面都是一片空白。
- 由于脚本会阻塞页面其他资源的下载,因此推荐将所有标签的底部,以尽量减少对整个页面下载的影响。
- 推荐的代码放置位置示例
- Hello world!
- 这段代码展示了在 HTML 文档中放置标签的推荐位置。尽管脚本下载会阻塞另一个脚本,但是页面的大部分内容都已经下载完成并显示给了用户,因此页面下载不会显得太慢。这是优化 JavaScript 的首要规则:将脚本放在底部。
- 组织脚本
- 由于每个标签,都会因执行脚本而导致一定的延时,因此最小化延迟时间将会明显改善页面的总体性能。
- 这个问题在处理外链 JavaScript 文件时略有不同。考虑到 HTTP 请求会带来额外的性能开销,因此下载单个 100Kb 的文件将比下载 5 个 20Kb 的文件更快。也就是说,减少页面中外链脚本的数量将会改善性能。
- 通常一个大型网站或应用需要依赖数个 JavaScript 文件。您可以把多个文件合并成一个,这样只需要引用一个标签,就可以减少性能消耗。文件合并的工作可通过离线的打包工具或者一些实时的在线服务来实现。
- 需要特别提醒的是,把一段内嵌脚本放在引用外链样式表的之后会导致页面阻塞去等待样式表的下载。这样做是为了确保内嵌脚本在执行时能获得最精确的样式信息。因此,建议不要把内嵌脚本紧跟在标签后面。
- 无阻塞的脚本
- 减少 JavaScript 文件大小并限制 HTTP 请求数在功能丰富的 Web 应用或大型网站上并不总是可行。Web 应用的功能越丰富,所需要的 JavaScript 代码就越多,尽管下载单个较大的 JavaScript 文件只产生一次 HTTP 请求,却会锁死浏览器的一大段时间。为避免这种情况,需要通过一些特定的技术向页面中逐步加载 JavaScript 文件,这样做在某种程度上来说不会阻塞浏览器。
- 无阻塞脚本的秘诀在于,在页面加载完成后才加载 JavaScript 代码。这就意味着在 window 对象的 onload事件触发后再下载脚本。有多种方式可以实现这一效果。
- 延迟加载脚本
- HTML 4 为标签会以默认的方式处理,也就是说会造成阻塞。然而,如果您的目标浏览器支持的话,这仍然是个有用的解决方案。
- defer 属性使用方法示例
- 带有 defer 属性的标签时开始下载,但不会执行,直到 DOM 加载完成,即onload事件触发前才会被执行。当一个带有 defer 属性的 JavaScript 文件下载时,它不会阻塞浏览器的其他进程,因此这类文件可以与其他资源文件一起并行下载。
- 任何带有 defer 属性的 的例子展示了defer属性如何影响脚本行为:
- defer 属性对脚本行为的影响
- alert("defer");
- alert("script");
- window.onload =function(){
- alert("load");
- };
- 这段代码在页面处理过程中弹出三次对话框。不支持 defer 属性的浏览器的弹出顺序是:"defer"、"script"、"load"。而在支持 defer 属性的浏览器上,弹出的顺序则是:"script"、"defer"、"load"。请注意,带有 defer 属性的元素不是跟在第二个后面执行,而是在 onload 事件被触发前被调用。
- 如果您的目标浏览器只包括 Internet Explorer 和 Firefox 3.5,那么 defer 脚本确实有用。如果您需要支持跨领域的多种浏览器,那么还有更一致的实现方式。
- HTML 5 为标签定义了一个新的扩展属性:async。它的作用和 defer 一样,能够异步地加载和执行脚本,不因为加载脚本而阻塞页面的加载。但是有一点需要注意,在有 async 的情况下,JavaScript 脚本一旦下载好了就会执行,所以很有可能不是按照原本的顺序来执行的。如果 JavaScript 脚本前后有依赖性,使用 async 就很有可能出现错误。
- 动态脚本元素
- 文档对象模型(DOM)允许您使用 JavaScript 动态创建 HTML 的几乎全部文档内容。元素与页面其他元素一样,可以非常容易地通过标准 DOM 函数创建:
- 通过标准 DOM 函数创建元素
- varscript = document.createElement ("script");
- script.type = "text/javascript";
- script.src = "script1.js";
- document.getElementsByTagName("head")[0].appendChild(script);
- 新的部分而不会对其余部分的页面代码造成影响(除了用于下载文件的 HTTP 连接)。
- 当文件使用动态脚本节点下载时,返回的代码通常立即执行(除了 Firefox 和 Opera,他们将等待此前的所有动态脚本节点执行完毕)。当脚本是"自运行"类型时,这一机制运行正常,但是如果脚本只包含供页面其他脚本调用调用的接口,则会带来问题。这种情况下,您需要跟踪脚本下载完成并是否准备妥善。可以使用动态 节点发出事件得到相关信息。
- Firefox、Opera, Chorme 和 Safari 3+会在节点接收完成之后发出一个 onload 事件。您可以监听这一事件,以得到脚本准备好的通知:
- 通过监听 onload 事件加载 JavaScript 脚本
- varscript = document.createElement ("script")
- script.type = "text/javascript";
- //Firefox, Opera, Chrome, Safari 3+ script.onload =function(){
- alert("Script loaded!");
- };
- script.src = "script1.js";
- document.getElementsByTagName("head")[0].appendChild(script);
- Internet Explorer 支持另一种实现方式,它发出一个 readystatechange 事件。元素有一个 readyState 属性,它的值随着下载外部文件的过程而改变。readyState 有五种取值:
- 微软文档上说,在元素会得到"loader"却从不出现"complete",但另外一些情况下出现"complete"而用不到"loaded"。最安全的办法就是在 readystatechange 事件中检查这两种状态,并且当其中一种状态出现时,删除 readystatechange 事件句柄(保证事件不会被处理两次):
- 通过检查 readyState 状态加载 JavaScript 脚本
- varscript = document.createElement("script")
- script.type = "text/javascript";
- //Internet Explorer script.onreadystatechange =function(){
- if(script.readyState == "loaded" || script.readyState == "complete"){
- script.onreadystatechange =null;
- alert("Script loaded.");
- }
- };
- script.src = "script1.js";
- document.getElementsByTagName("head")[0].appendChild(script);
- 大多数情况下,您希望调用一个函数就可以实现 JavaScript 文件的动态加载。下面的函数封装了标准实现和 IE 实现所需的功能:
- 通过函数进行封装
- function loadScript(url, callback){
- varscript = 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("head")[0].appendChild(script);
- }
- 此函数接收两个参数:JavaScript 文件的 URL,和一个当 JavaScript 接收完成时触发的回调函数。属性检查用于决定监视哪种事件。最后一步,设置 src 属性,并将元素添加至页面。此 loadScript() 函数使用方法如下:
- loadScript()函数使用方法
- loadScript("script1.js",function(){
- alert("File is loaded!");
- });
- 您可以在页面中动态加载很多 JavaScript 文件,但要注意,浏览器不保证文件加载的顺序。所有主流浏览器之中,只有 Firefox 和 Opera 保证脚本按照您指定的顺序执行。其他浏览器将按照服务器返回它们的次序下载并运行不同的代码文件。您可以将下载操作串联在一起以保证他们的次序,如下:
- 通过 loadScript()函数加载多个 JavaScript 脚本
- loadScript("script1.js",function(){
- loadScript("script2.js",function(){
- loadScript("script3.js",function(){
- alert("All files are loaded!");
- });
- });
- });
- 此代码等待 script1.js 可用之后才开始加载 script2.js,等 script2.js 可用之后才开始加载 script3.js。虽然此方法可行,但如果要下载和执行的文件很多,还是有些麻烦。如果多个文件的次序十分重要,更好的办法是将这些文件按照正确的次序连接成一个文件。独立文件可以一次性下载所有代码(由于这是异步进行的,使用一个大文件并没有什么损失)。
- 动态脚本加载是非阻塞 JavaScript 下载中最常用的模式,因为它可以跨浏览器,而且简单易用。
- 使用 XMLHttpRequest(XHR)对象
- 此技术首先创建一个 XHR 对象,然后下载 JavaScript 文件,接着用一个动态 是一个简单的例子:
- 通过 XHR 对象加载 JavaScript 脚本
- varxhr =new XMLHttpRequest();
- xhr.open("get", "script1.js",true);
- xhr.onreadystatechange =function(){
- if(xhr.readyState == 4){
- if(xhr.status >= 200 && xhr.status < 300 || xhr.status == 304){
- varscript = document.createElement ("script");
- script.type = "text/javascript";
- script.text = xhr.responseText;
- document.body.appendChild(script);
- }
- }
- };
- xhr.send(null);
- 此代码向服务器发送一个获取 script1.js 文件的 GET 请求。onreadystatechange 事件处理函数检查 readyState 是不是 4,然后检查 HTTP 状态码是不是有效(2XX 表示有效的回应,304 表示一个缓存响应)。如果收到了一个有效的响应,那么就创建一个新的元素被添加到文档,代码将被执行,并准备使用。
- 这种方法的主要优点是,您可以下载不立即执行的 JavaScript 代码。由于代码返回在标签约束),它下载后不会自动执行,这使得您可以推迟执行,直到一切都准备好了。另一个优点是,同样的代码在所有现代浏览器中都不会引发异常。
- 此方法最主要的限制是:JavaScript 文件必须与页面放置在同一个域内,不能从 CDN 下载(CDN 指"内容投递网络(Content Delivery Network)",所以大型网页通常不采用 XHR 脚本注入技术。
- 总结
- 减少 JavaScript 对性能的影响有以下几种方法:
- 通过以上策略,可以在很大程度上提高那些需要使用大量 JavaScript 的 Web 网站和应用的实际性能。
- 原文来自:https://www.ibm.com/developerworks/cn/web/1308_caiys_jsload/
- 补充js加载函数:
- function loadJs(url, callback, charset) {
- varhead = document.getElementsByTagName("head")[0];
- varscript = document.createElement("script");
- if( !!charset) script.charset = "utf-8";
- script.src = url;
- script.onload = script.onreadystatechange =function() {
- varf = script.readyState;
- if(f && f != "loaded" && f != "complete")return;
- script.onload = script.onreadystatechange =null;
- head.removeChild(script) if (callback) {
- callback() || callback
- };
- };
- head.appendChild(script);
- }
- // js同步加载
- function getScripts(i, linkArray, fn) {
- env || getEnv();
- varscript = document.createElement('script');
- script.type = 'text/javascript';
- script.src = linkArray[i];
- varhead = document.head || document.getElementsByTagName('head')[0];
- head.appendChild(script);
- if(env.ie && 'onreadystatechange'inscript && !('draggable'inscript)){//ie浏览器使用以下方式加载 script.onreadystatechange =function () {
- if(/loaded|complete/.test(script.readyState)) {
- script.onreadystatechange =null;
- if(i === linkArray.length-1) {
- if (fn) {
- fn();
- }
- } else {
- getScripts(++i, linkArray, fn);
- }
- }
- };
- }else{
- script.onload =function() {
- if(i === linkArray.length-1) {
- if (fn) {
- fn();
- }
- } else {
- getScripts(++i, linkArray, fn);
- }
- };
- }
- }
来源: