`

《仔仔细细分析Ext》 Ext的事件机制源码详解:Ext.onReady()方法的原理是什么?

阅读更多

导言

       Ext 封装了自己的事件机制,除了支持原始的鼠标和键盘事件之外,还能支持高级语义的事件,如 load beforeLoad render destory 等等。

         这一部分内容专门来解析 Ext 的事件机制,我打算用四种方法来解析这部分内容,力求稍有 js 基础的人都能看懂: 1 、源码注释翻译; 2 、执行流程图和源码片段截图; 3 、简单的 UML 类图; 4 、自定义测试代码。

         这部分内容从最受大家关注的 Ext.onReady() 这个方法开始。 OK ,不要害怕,我会确保你能看懂的, Let’s go!

Ext.onReady()

         大家经常会这么开始 Ext 的代码:

         Ext.onReady(function(){

                   //……

})

这么做的目的是为了确保浏览器在文档加载完成之后再来执行我们的 js 。为了能更快更方便地理解 Ext.onReady() 这个方法到底做了什么,我们先来整理一下自己的思路。

书接上文,在“《仔仔细细分析 Ext Ext 的事件模型之一 小沈阳的烦恼”一文中,我们自己制作了一段粗糙的代码来模拟这个过程,思路很简单:

第一步:先做一个简单的数组 var funcs=[] ,这个数组用来存放所有需要在文档一加载完成就开始执行的方法;

第二步:自己写一个叫 run() 的方法。这个方法只做一件事,它把第一步保存在 funcs 数据里面的函数全部拿出来依次执行一遍。

第三步:使用 js 原生的事件注册方式把第二步的 run() 方法注册到 window.onload 事件上。这样,一旦文档加载完成,这个方法就被执行。这样,我们预先放在 funcs 数组里面的方法就全部被执行了。

这段代码完整拿来看看:(原文请翻阅我的 blog:damoqiongqiu.iteye.com

 

 

Js代码 复制代码
  1. <SPAN style="FONT-SIZE: medium">MyExt={                               // 全局静态对象   
  2.   
  3.          version:'1.0',   
  4.   
  5.          isReady:false,           // 文档是否加载完成标志   
  6.   
  7.          funcs:[],                      // 缓存函数的数组   
  8.   
  9.            
  10.   
  11.          onReady:function(f){   
  12.   
  13.                    if(MyExt.isReady) f();   
  14.   
  15.                    else MyExt.funcs.push(f);   
  16.   
  17.          },   
  18.   
  19.            
  20.   
  21.          run:function(){   
  22.   
  23.                    if(MyExt.isReady)    return;   
  24.   
  25.                    for(var i=0;i<MyExt.funcs.length;i++){   
  26.   
  27.                             try{// 为了避免一个方法的错误影响到其它方法,一定要有 try 和 catch   
  28.   
  29.                                      var f=MyExt.funcs[i];   
  30.   
  31.                                      f();   
  32.   
  33.                             }catch(e){   
  34.   
  35.                                      alert(" 事件发生异常了 "+e);   
  36.   
  37.                             }   
  38.   
  39.                    }   
  40.   
  41.                    MyExt.isReady=true;   
  42.   
  43.                    funcs=[];// 清空一下   
  44.   
  45.          }   
  46.   
  47. };   
  48.   
  49.     
  50.   
  51.     
  52.   
  53. if(window.addEventListener){// 标准的 DOM2 事件注册   
  54.   
  55.          window.addEventListener("load",MyExt.run,false);   
  56.   
  57. }else if(window.attachEvent){//IE 的事件注册   
  58.   
  59.          window.attachEvent("onload",MyExt.run);   
  60.   
  61. }else {// 原始的 DOM0 的事件注册   
  62.   
  63.          window.onload=MyExt.run;   
  64.   
  65. }    
  66.   
  67. </SPAN>  
MyExt={                               // 全局静态对象

         version:'1.0',

         isReady:false,           // 文档是否加载完成标志

         funcs:[],                      // 缓存函数的数组

        

         onReady:function(f){

                   if(MyExt.isReady) f();

                   else MyExt.funcs.push(f);

         },

        

         run:function(){

                   if(MyExt.isReady)    return;

                   for(var i=0;i<MyExt.funcs.length;i++){

                            try{// 为了避免一个方法的错误影响到其它方法,一定要有 try 和 catch

                                     var f=MyExt.funcs[i];

                                     f();

                            }catch(e){

                                     alert(" 事件发生异常了 "+e);

                            }

                   }

                   MyExt.isReady=true;

                   funcs=[];// 清空一下

         }

};

 

 

if(window.addEventListener){// 标准的 DOM2 事件注册

         window.addEventListener("load",MyExt.run,false);

}else if(window.attachEvent){//IE 的事件注册

         window.attachEvent("onload",MyExt.run);

}else {// 原始的 DOM0 的事件注册

         window.onload=MyExt.run;

} 




 

 

// 这样,我们就可以这么来写自己的代码,而不必关心文档加载的问题了。

MyExt.onReady(function(){

         // 自己的代码

})

 

 

         这段代码虽然简陋,但是核心的原理已经出来了:既然文档不加载完成我没办法干活,那好了,我不跟你纠缠,我先把要干的活都先存到一个筐子里面,等你(浏览器)觉得文档加载完了,可以干活了,你自己到这里来拿这些东西,一次性都给我干了吧!(貌似挺像批处理的撒!)

OK ,到这里应该没有什么难缠的地方吧?那么 OK ,恭喜你!因为不出意外的话,你肯定能很透彻地理解 Ext.onReady() 了!

Ext 采取的是类似的策略,只是实现的细节有所不同,下面我们来逐步深入地去解析。(下面的内容请不要跳着看。)

 

首先看一下 core 包下的 EventManage.js ,它里面有这么一句 :



很简单吧?原来 Ext.onReady 实际上执行的是 Ext.EventManager.onDocumentReady 这个方法。好,我们顺藤摸瓜,来看看 onDocumentReady 又是何方神圣。

 

原创作者: damoqiongqiu   阅读:366次   评论:1条   更新时间:2009-06-18     收藏

  

   

 

     还好,这段代码也不多。先不要管其它的东西,我们先来看这段代码本身的流程,然后再来看各个细节是怎么实现的:

 

如果 docReadyEvent 这个对象为空,就去执行 initDocReady() 这个方法;

 

接下来再判断,如果 docReadyState 或者 Ext.isReady 这两个标志位为真,立即执行传递进来的 function ,或者按照 options 这个参数里面的 delay 属性的值延迟执行传进来的 function

 

备注: defer 方法是 Ext 对原生 Function 类的扩展,每个函数都有这个方法,作用是延迟一定的时间再执行。详细描述请翻阅我的 blog :“《仔仔细细分析 Ext

Ext Function 类的扩展” )。

 

         否则(文档还没初始化好),把我们传递进来的函数作为一个监听器,添加到 docReadyEvent 这个事件里面去。

 

结合我们自己做的那个小例子,到这个地方我们可以很敏感地推测出来,在添加监听器这个地方 Ext 肯定把我们传递进来的方法放到某个数组里面去了,然后等文档加载完成的时候再来调用这些方法,就像我们自己做的那样。

 

         只是,这里的数组它不是一个简单的数组,而是作为 docReadyEvent 的一个属性出现的。来看看 docReadyEvent 到底是谁。沿着源码向上查找,我们发现了 docReadyEvent 的身影:



 


原来它是 Ext.util.Event 的一个实例,好,我们把 Ext.util.Event 揪出来(它是在 util 包下的 Observable.js 这个文件里面定义的):



 

 

    哈哈,原来它是如此简单的一个对象!我们的推断没错,它里面果然用了一个数组 (listeners) 来放我们的 function !至于它的 addListener() 方法和最终怎么去调用我们传递进来的方法的(看它的 fire() 方法),这里不多罗嗦了,代码也很简单,原理就和我们自己制作的那个小例子相同。

        

         好,到这里,感觉还没有什么太特别的地方。既然 Ext 在文档没有加载完的时候把我们的 function 存起来了,那么,在文档加载完之后它又是在哪里调用的呢?这个调用肯定与上面我们略过的 initDocReady() 这个方法有关,看代码:

Js代码 复制代码
  1. //检测文档是否加载完成   
  2.     var initDocReady = function(){   
  3.         docReadyEvent = new Ext.util.Event();   
  4.   
  5.         if(Ext.isReady){   
  6.             return;   
  7.         }   
  8.   
  9.         // no matter what, make sure it fires on load   
  10.         //确保文档加载完成事件能触发(这样fireDocReady()方法才能执行,从能设置文档状态并清除轮询定时器)   
  11.         E.on(window, 'load', fireDocReady);   
  12.            
  13.         if(Ext.isGecko || Ext.isOpera) {//Gecko和Opera下的文档加载完成检测方法   
  14.             document.addEventListener('DOMContentLoaded', fireDocReady, false);   
  15.         }   
  16.         else if(Ext.isIE){//IE下的文档加载完成检测方法   
  17.             docReadyProcId = setInterval(function(){   
  18.                 try{   
  19.                     //这一句一直抛出异常,直到文档加载完成   
  20.                     Ext.isReady || (document.documentElement.doScroll('left'));   
  21.                 }catch(e){   
  22.                     return;   
  23.                 }   
  24.                 fireDocReady();  //没有异常抛出,则说明文档已经加载完成,手工fire文档加载完成事件   
  25.             }, 5);   
  26.   
  27.             document.onreadystatechange = function(){   
  28.                 if(document.readyState == 'complete'){   
  29.                     document.onreadystatechange = null;   
  30.                     fireDocReady();   
  31.                 }   
  32.             };   
  33.         }   
  34.         else if(Ext.isSafari){//Safari下的文档加载完成检测方法   
  35.             docReadyProcId = setInterval(function(){   
  36.                 var rs = document.readyState;   
  37.                 if(rs == 'complete') {   
  38.                     fireDocReady();   
  39.                  }   
  40.             }, 10);   
  41.         }   
  42. };  
//检测文档是否加载完成
    var initDocReady = function(){
        docReadyEvent = new Ext.util.Event();

        if(Ext.isReady){
            return;
        }

        // no matter what, make sure it fires on load
        //确保文档加载完成事件能触发(这样fireDocReady()方法才能执行,从能设置文档状态并清除轮询定时器)
        E.on(window, 'load', fireDocReady);
		
        if(Ext.isGecko || Ext.isOpera) {//Gecko和Opera下的文档加载完成检测方法
            document.addEventListener('DOMContentLoaded', fireDocReady, false);
        }
        else if(Ext.isIE){//IE下的文档加载完成检测方法
            docReadyProcId = setInterval(function(){
                try{
                	//这一句一直抛出异常,直到文档加载完成
                    Ext.isReady || (document.documentElement.doScroll('left'));
                }catch(e){
                    return;
                }
                fireDocReady();  //没有异常抛出,则说明文档已经加载完成,手工fire文档加载完成事件
            }, 5);

			document.onreadystatechange = function(){
				if(document.readyState == 'complete'){
					document.onreadystatechange = null;
					fireDocReady();
				}
            };
        }
        else if(Ext.isSafari){//Safari下的文档加载完成检测方法
            docReadyProcId = setInterval(function(){
                var rs = document.readyState;
                if(rs == 'complete') {
                    fireDocReady();
                 }
            }, 10);
        }
};
 

 

这段代码看似很长,实际上做的事情并不多。为了兼容浏览器,里面做了三个判断,我们来看最好懂的 IE 的情况,其它的都可以类比。

 



 总体来看,它就两小段代码,第一段实际上是个定时器,每隔 5 毫秒就去尝试让 document 的滚动一下,如果能滚动(也就是 document 已经初始化完成了)就执行 fireDocReady 。从名称可以看出, fireDocReady 方法是在触发“文档准备好了”这个事件。可以推断,在这个方法里面一定会调用我们传进来的那些(被缓存)的方法。



 看到第 119 行的 docReadyEvent.fire() 方法了吧,它先做了一些扫尾的工作,设置 Ext.isReady 标志位啦、清除定时器啦等等,然后就去调用了我们的 function

        

         好了,解释到这个地方,其它的代码就无需多言了,应该都能轻松地看明白了。这里也提出一个小小的疑问:在 initDocReady() 这个方法里面,是用一个定时器来不断“重复错误”的方式来检测 document 是不是已经加载完成的。我曾经记得某本“经典”的 JAVA C++ 书籍里面告诫程序员,不要企图使用“抛异常”的方式来做常规的、正常的流程判断。不知道有没有人研究过这块“尝试错误”的代码是不是存在什么样的问题(效率或内存)?如果你知道的话,请一定记得告诉我一声(群 88403922 )。

分享到:
评论
1 楼 xiao_Yao 2010-11-04  
"。我曾经记得某本“经典”的 JAVA 或 C++ 书籍里面告诫程序员,不要企图使用“抛异常”的方式来做常规的、正常的流程判断。"

联系实际的情况来说,我觉的这里的“抛异常”的方式使用是合理的。楼主偏颇了。

相关推荐

    Ext Js权威指南(.zip.001

    9.2.2 ext.container.abstractcontainer和ext.container.container的配置项、属性、方法和事件 / 434 9.2.3 将body元素作为容器:ext.container.viewport / 435 9.3 面板 / 436 9.3.1 面板的结构 / 436 9.3.2 ...

    Backbone.Events.onReady:Backbone.Events 的 onReady() 方法

    将onReady和triggerReady方法添加到Backbone.Events 。 行为: 准备好之前- 注册回调。 准备就绪时,运行所有挂起的回调,从triggerReady传入任何数据。 准备就绪后- 立即运行回调,传入缓存数据。 ###API 以下...

    Ext原理.txt

    ### Ext原理知识点详解 #### 一、Ext简介与原理 **Ext** 是一款基于 JavaScript 的开源框架,主要用于 Web 应用程序的开发。它提供了一系列丰富的用户界面组件和功能,帮助开发者快速构建交互性强、用户体验良好的...

    EXT dojochina Ext类静态方法.rar

    EXT dojochina Ext类静态方法是一个关于EXT框架在JavaScript中的使用的主题,主要聚焦于Ext类的静态方法。EXT是一个强大的前端开发框架,由Sencha公司开发,它提供了丰富的组件库,用于构建复杂的Web应用程序。在EXT...

    EXT中文手册.pdf

    1. **Ext的基本配置和初始化**:在Ext框架中,`Ext.onReady`是一个非常核心的方法,它用于确保在DOM完全加载后执行指定的函数。通过`Ext.onReady`,可以初始化Ext应用并执行必要的配置。例如:`Ext.onReady(function...

    ExtJS Ext.MessageBox.alert&#40;&#41;弹出对话框详解

    代码如下: Ext.onReady(function() { Ext.Msg.alert&#40;‘提示’, ‘逗号分隔参数列表’&#41;; //这种方式非常常见的 }); 效果图: 代码如下: Ext.onReady(function() { //定义 JSON(配置对象) var config = { ...

    ext表格布局小例子

    ### ExtJS 表格布局小例子详解 #### 一、简介 本文档旨在解析一个具体的 ExtJS 表格布局小例子,重点介绍其中涉及的数据存储、数据删除与添加的方法。ExtJS 是一款强大的 JavaScript 框架,用于构建交互式的前端...

    EXT dojochina Ext类实例方法.rar

    Ext类实例方法是用于操作和管理这些组件的方法,它们是EXT JS框架的核心组成部分。下面将详细介绍一些常见的Ext类实例方法: 1. **Ext.application()**: 这是启动EXT JS应用程序的主要方法,它负责配置应用、加载所...

    Ext_Js分页显示案例详解

    ### Ext_Js分页显示案例详解 #### 一、引言 在Web开发领域,Ext_Js(简称ExtJS)是一种强大的JavaScript库,用于构建复杂的客户端应用。它提供了一套丰富的UI组件,使得开发者能够轻松地创建美观且功能强大的用户...

    ext培训第二讲.doc

    #### 三、Ext.onReady方法详解 **3.1 Ext.onReady方法介绍** - **功能**:`Ext.onReady`方法用于确保某个函数在文档初始化完成后执行(即在页面的所有元素都已加载完毕,但图片等非关键资源可能尚未加载完成时)。...

    ext使用--Panel和iframe联合使用时页面高度的解决方法

    Ext.onReady(function() { var iframe = Ext.create('Ext.ux.IFrame', { src: 'http://example.com', plugins: ['miframe'] // 使用miframe插件 }); var panel = Ext.create('Ext.Panel', { items: [iframe]...

    EXT核心API详解.doc

    EXT核心API详解主要涵盖了一系列用于构建富客户端应用的JavaScript库函数和方法。这些API提供了丰富的功能,包括元素操作、事件处理、数据序列化与反序列化、类继承以及组件管理等。以下是对EXT核心API中提及的一些...

    ext实例 ext操作步骤

    EXT是一个强大的JavaScript库,专用于构建富客户端应用程序。它的全称是Ext JS,提供了一整套用户界面组件和布局管理工具,使得开发者可以构建出功能丰富、交互性强的Web应用。EXT实例通常涉及到一系列操作步骤,...

    EXT入门详解.pdf

    通过这个简单的例子,我们可以了解到EXT的基本结构和工作原理,包括如何引入EXT库、设置样式、监听和响应用户事件。随着深入学习,EXT的Grid、Panel、Form、Window等组件,以及布局管理、数据绑定等功能将为开发复杂...

    ext入门必学

    ### ext入门必学知识点详解 #### 一、Ext框架概览与起源 **知识点1:Ext框架起源** Ext最初作为YUI(Yahoo User Interface Library)的扩展存在,名为YUI.Ext,随后发展成为一个独立的JavaScript框架,不再依赖于...

    3------通过实例学习------Ext.js------.docx

    6. **src**:未压缩的源代码目录,开发者可以查看源码理解内部工作原理,方便调试和定制。 在实际开发中,我们需要引入以下文件: - **bootstrap.js**:这是启动Ext.js框架的基本脚本,通常在HTML页面中首先引入。...

    EXT 时间控件

    EXT 时间控件是一种在网页应用中用于用户交互的组件,主要功能是提供一个方便的时间选择界面,提升用户体验。EXT 是 Sencha 公司开发的一套前端框架,它提供了丰富的组件库,包括表格、面板、窗口、按钮等,EXT 时间...

Global site tag (gtag.js) - Google Analytics