• 1.加载和运行


    前记:js的出现给人们上网时的交互体验,但是一直有个地方被人们所诟病的是拖慢了网页的运行速度。
    而且传统方式下,浏览器下载和运行js代码都是属于阻塞式的,会很消耗浏览器的运行时间。
    为了提高网页运行速率,减少用于等待页面的时间 。很多人提出了很多种js加载和运行的方式。
    以下方法出自《High Performance JavaScript》中文翻译版的《高性能JavaScript》。

    基本工作原理:

    1、大多数浏览器都是单线程处理UI更新和js运行等多个任务,也就说同一时间只能有一个任务被执行。

    2、浏览器在遇到<script>标签时,都会停下来运行js代码,然后在进行页面解析和翻译页面。用src属性加载外部js文件时也会完全阻塞页面解析和用户交互。

    3、浏览器在遇到<body>之前,不会渲染页面的任何部分。

    注:目前IE8+,FF3.5+,Safari4+ , chrome2+(也就说现在(注明今年是2015年)的机会所有浏览器)都支持并行下载Javascript文件。但是JavaScript文件的下载还是会阻塞其他资源的下载,例如图片。

    基于前人的工作经验,和本书中提出的方法,可以做的一些操作是:

    (1)将Js脚本放在底部,</body>之前就好。(Yahoo优越性能小组关于Javascript第一条定律)

    (2)减少<script>标签数。

    (3)打包js文件组。

    对于外部js文件,每个HTTP请求都会产生额外的性能负担。比如下载一个100KB的文件要比下载四个25KB的文件要快。

    可以利用打包工具打包js文件,Yahoo提供了一个实时工具。(Yahoo!combo handler.)


    非阻塞脚本

    非阻塞的思路是:等页面都加载完成以后,在加载js源码。在window的load事件发生以后再开始下载代码。

    (1) 延迟脚本defer

    defer属性是HTML4中给出的,指明元素中所包含的脚本不打算修改DOM,代码可以稍后执行。

    特性是:js文件在script被解析时下载,但代码不被执行,直到DOM都加载完成(在window的load事件句柄被调用之前执行)。

    而且在下载js文件的时候,不阻塞其他处理过程,可以和页面的其他资源一起并行下载。

    但是支持defer的浏览器不多。

    笔者自己测试下来,只有IE(测试的是IE8)支持defer,FireFox(测试的是FF42),chrome(测试的是chrome46),Opera(测试的是Opera33),Safari(测试的是Safari5.34)都不支持
    所以这个方法虽然简单,但是浏览器们都不支持啊!

    (2)动态脚本元素Dynamic Script Elements(非阻塞js下载中最常用的模式)

    <script>
    var script = document.createElement("script");
    script.src = "f1.js";

    script.type = "text/javascript";
    document.getElementsByTagName("head")[0].appendChild("script");
    </script>

    无论在何处启动下载,文件的下载和运行都不会阻塞其他页面处理过程。甚至可以将代码放在<head>内部而不会对其余部分的页面代码造成影响(除了用于下载文件的HTTP连接)。

    当加载js文件采用DSE(动态脚本元素的简称)时,下载完成后返回的代码会立即执行(Opera和FireFox除外,他们会等待此前的所有动态脚本执行完毕)。所以,当返回代码是自执行代码就ok。

    但是如果返回的脚本代码只是其他脚本的调用的接口,则会出现问题。(什么问题呢??待追

    所以在这种情况下,需要自己去追踪脚本的处理进度(事件监听),是否已经准备好来使用。

    IE浏览器可以发出readystatechange事件,还给script元素提供了readyState属性,有5个值:

    • "uninitialized":默认状态,还未开始初始化
    • "loading":下载已经开始
    • "loaded":下载完成
    • "interactive":下载完成但尚不可用
    • "complete": 所有数据都已准备好,可以使用

    一旦监听到上面的5个值之一,在使用完readystatechange以后,就需要删除readystatechange事件句柄。(script.readystatechange==null,保证事件不会被处理两次)

    在FireFox,Safari3+,Opera中,会发出load事件。

    //兼容各个浏览的加载函数
    function loadScript( url , callback )
    {
       var script = document.createElement("script");
       
       script.src = "text/javascript";
       if( script.readyState )
       {  
    	//IE
          script.readystatechange = funciton()
         {
             if( script.readyState == "loaded" || script.reayState == "complete")
             {
    		    script.readystatechange = null;
    			callback();
             }
         }
        }
    	else
    	{
    		//非IE
    		script.onload = function()
    		{
    		  callback();
    		}
    	}
       script.src = url;    
       document.getElementByTagName("head")[0].appendChild(script);
    }       
    

     用这种方式加载js文件,浏览器不能保证文件加载顺序(Firefox和Opera除外)。

    要保证文件执行顺序,可以使用嵌套的callback,但是文件过多的情况下不推荐使用。如果文件顺序很重要而且文件很多,则可以将文件打包成一个大文件。

    (3)XHR脚本注入方式(大型网页不采用)

    方法是:

    第一:创建XHR对象注入页面中

    第二:下载js文件

    第三:动态创建script注入页面中

    var xhr = window.XMLHTTPRequest? new window.XMLHTTPRequest():new ActiveXObject("Microsoft.XMLHTTP");
    xhr.open("f1.js",'get',false);
    xhr.send();
    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);
         }
      }
    }  

    优点:

    • 可以下载不立即执行js代码,可以推迟执行,直到一切准备好。
    • 在所有浏览器中,都不会引发异常。

    缺点:

    • 无法跨域实现,js文件与页面必须在同一个域内。

    该书推荐的nonblocking Pattern(非阻塞模式)

    加载和运行js文件分两步:

    第一步:将极必要且很小的code采用动态加载js的方式实现,主要保证加载的js内容尽量小,小到只剩下loadScript()函数。

    第二步:然后加载其余的初始化js代码

    <script src = "loadScript.js" type = "text/javascript">
    <script>
    loadScript("the-rest.js",function(){ application.init(); });
    </script>

    将这段代码放置在</body>之前,好处是:不会阻塞页面其他部分的显示;当第一部分js文件loading完,所有需要的DOM节点,都已经创建完成,并准备好被访问。

    可以避免额外的事件处理window.onload,来告知页面是否准备好了。

    另外一种选择是,直接将loadScript函数嵌入到页面中,避免HTTP请求。(如果采用这种方式,建议使用"YUI Compressor"或类似工具将脚本压缩到最小字节尺寸)。一旦页面初始化代码下载完成,还可以使用loadScript()函数加载页面所需的额外功能函数。


    其他(一些开源的插件)

    (1)YUI3:由一段很小的初始化代码组成,用于下载其余的功能代码。

    需要在页面中加载YUI的种子文件。(6KB,gzipped)

    <script type = "text/javascript" src = "http://yui.yahooapis.com/combo?3.0.0/build/yui/yui-min.js"></script>


    (2)The LazyLoad (精缩以后只有1.5KB)

    <script type = "text/javascript" src = "lazyload-min.js"></script>
    <script>
    LazyLoad.js("f1.js",function(){      //多个就参数文件数组:["f1.js","f2.js"...]
        application.init();
    })
    </script>

    能够下载多个js文件,并能够保证在所有的浏览器上按照正确顺序执行。

    注:即使使用非阻塞方式下载,仍然建议较少js文件数量,因为每次下载一个文件仍然是一个单独的HTTP请求,回调函数callback()在所有文件下载并执行完成后执行。

    (3)LABjs (精缩后4.5KB)(链式操作)

    常用接口:

    script('f1.js') : 下载f1文件

    wait(fn) : 在js文件下载并执行完成以后,执行fn函数

    //以这种连续方式下载f1,f2两个js文件,无法保证f1先执行。
    LABjs.script("f1.js").script("f2.js").wait(function() {
       application.init(); 
    });
    
    //可以保证f2在1之后执行,但是两者是并行下载的
    LABjs.script("f1.js").wait().script("f2.js").wait(function() { application.init(); });
    

     LABjs最大的特色就是:管理依赖关系。

    其中的wait()函数可以指定哪些文件应该等待其他文件执行完在执行。

    后记:以上这些纯粹都是一些理论分析,笔者还没有真正实践测试过。所谓实践出真知,等笔者学会了后台技术(捂脸。。。)再依次好好测试各种方法的优缺点及性能。
  • 相关阅读:
    log4j
    【表单验证】几个常用的正则表达式子
    【代码健壮性】善用data-属性来关联,慎用parent()之类的查找结构
    【javascript闭包】转载一篇不错的解释,也有几个大牛的链接
    【CSS】三栏布局的经典实现
    【转载】sublime text3 全攻略
    【CSS】text-align:justify 的使用
    javascript与jquery删除元素节点
    【Jquery对象】jquery与dom对象的区别
    【Jquery】this和event.target的区别
  • 原文地址:https://www.cnblogs.com/shixiaomiao/p/5005618.html
Copyright © 2020-2023  润新知