Pages

搜尋此網誌

2013年12月25日 星期三

extjs: DOM、Element 以及 Component 的差別

extjs: DOM、Element 以及 Component 的差別

資料來源:

學習及應用 extjs,需要理解 Html DOM、Ext Element及Component 三者的區別。extjs 是 Wed 前端框架,基於標準 W3C 構建設的,使用到的都是HTML、CSS、DIV 等相關技術。無論元件有多少屬性、還是事件、方法等,其最終都會轉化為 HTML 在瀏覽器上顯示出來,而每一個 HTML 頁面都有 DOM,瀏覽器中的所有內容都有相應的 DOM,動態改變頁面的內容正是透過 javascript 來操作 DOM 進行實現。

而在 extjs 中就有個專門用來處理 DOM 的類別 …

Ext.DomHelper

DOM其實就是 W3C 標準定義的原始 API,在 extjs 中對 dom 進行強化,製作了 Ext.DomHelper

在官方 API 中可以看到該類別屬於 singleton,所謂的 singleton 就是所謂的整個程式在運行時只有一份,且不需宣告即已被實體化,官方文件中的範例:

var dh = Ext.DomHelper; // create shorthand alias
// specification object
var spec = {
    id: 'my-ul',
    tag: 'ul',
    cls: 'my-list',
    // append children after creating
    children: [     // may also specify 'cn' instead of 'children'
        {tag: 'li', id: 'item0', html: 'List Item 0'},
        {tag: 'li', id: 'item1', html: 'List Item 1'},
        {tag: 'li', id: 'item2', html: 'List Item 2'}
    ]
};
var list = dh.append(
    'my-div', // the context element 'my-div' can either be the id or the actual node
    spec      // the specification object
);

可以看到 var dh = Ext.DomHelper; 並未透過 new 來進行建立實體,而在 Ext.DomHelper 特別針對了原生 javascript 的 Insertion methods 進行封裝,如下:

  • append
  • insertBefore
  • insertAfter
  • overwrite
  • createTemplate
  • insertHtml

當然 extjs 不只是單純的進行封裝那麼簡單,他還避免了一些在效能上容易造成問題的地方,比如說在我另一篇文章Javascript: Performance & Tip 有提到的 『9. DOM』 使用 DocumentFragment 避免 ReFlow 的問題,在 extjs 中已採用此作法,可以從原始碼DomHelper-more 中的 createDom 看到 el = doc.createDocumentFragment(); 使用了 DocumentFragment。

而DOM是W3C標準定義的原始API,如果我們要取得 dom 可以透過 Ext.getDom(el),除了用 dom id 來索引之外,也可以傳入 Element,如:

// gets dom node based on id
var elDom = Ext.getDom('elId');
// gets dom node based on the dom node
var elDom1 = Ext.getDom(elDom);

// If we don't know if we are working with an
// Ext.Element or a dom node use Ext.getDom
function(el){
    var dom = Ext.getDom(el);
    // do something with the dom node
}

接著來看在 extjs 對 DOM 進行封裝後的物件帶來什麼樣的功能 …

Ext.Element

Ext.Element 是Ext的底層API,主要是由Ext或自定義組件來進行呼叫。並且將 DOM 進行封裝,使用方式如下:

// by id
var el = Ext.get("my-div");

// by DOM element reference
var el = Ext.get(myDivElement);

可以看到只要透過 Ext.get 取得的物件,將會作為 Ext.Element 回傳。

透過 extjs 將 DOM 封裝為 Ext.Element,通過其操作 DOM 來實現頁面的效果顯示,可以方便做到若要把頁面中的某一個節點移到其它位置,要給某一個節點添加陰影效果,要隱藏或顯示某一個節點等,可以很輕易的執行相關的動畫特效,主要是因為在 Ext.Element 中包含了專門處理特效的類別:Ext.Fx,而以往都需要通過幾句 javascript 才能完成。舉例來說,下面這段取得 DOM 的語法:

var el = Ext.get("my-div");

// no animation
el.setWidth(100);

只是單純的將寬度設為 100 ,若要有類似特效的效果,很簡單:

// default animation
el.setWidth(100, true);

修改為上面的程式碼即可,詳細可參照 API-Ext.Element

因此,Ext 在 DOM 的基礎上創建了 Ext.Element,可以使用 Element 來包裝任何 DOM;Element 類別中添加了一系列快捷、簡便的實用方法。其中對於事件上的使用,不像 Component 中的事件是屬於 Ext events,在 Element 中的事件是屬於 browser events,在 extjs 官方 api 中有提到:

Note that the events documented in this class are not Ext events, they encapsulate browser events.

其中因為是屬於原生的 browser events 所以不是所有的事件在較舊的 browser 中都有支援,不過如果是屬於 Ext events 的範圍則都有支援。如官方所言:

Some older browsers may not support the full range of events. Which events are supported is beyond the control of ExtJs.

extjs 實作了 Element 來對原生的 dom 操作進行了強化,並且透過 Element 的使用可以不必擔心跨瀏覽器的相容性,解決前端令人困擾的問題,但對於終端用戶來說,僅僅有Element是不夠的,也因此 extjs 最強大的部份就是 …

Ext.Component

假設我們需要在開發時要顯示一個表格、樹狀結構或者彈出一個視窗等,為了方便建立相關的元件,除了 Element 以外,Extjs 還建立了一系列的前端顯示元件 Component,我們在撰寫程式時,只要使用這些組件 Componet 即可實現相關數據展示及交互等,而 Component 是較高層次的抽象,每一個組件在渲染 render 的時候,都會依次通過 Element、DOM 來生成最終的頁面效果。

在Ext中,組件 render 以後可以通過存取 Component 的 properties api-el 屬性來得到 Component 對應的 Element ,通過存取 Element 的 dom 屬性可以得到其下面的 DOM。

其中如果要取得某元件可以透過 Ext.getCmp 來得到,相關詳細說明可參考我的另一篇文章:Extjs: Component 的使用,找尋 Parent & Child

而下面範例說明實際上的使用狀況:

var view=new Ext.Viewport();                
//建立了一個組件 Component 
view.el.setOpacity(.5);                     
//呼叫 Element 的 setOpacity 方法
view.el.dom.innerHTML="Hello Ext";  //透過 Element 的 dom 屬性操作 DOM

以及如何分別取得 dom、element 以及 component

var win=new Ext.Window({id:"win1",title:"my window",width:200,height:200}); 
win .show(); 
var c=Ext.getCmp("win1");           //得到 win 
var e=Ext.get("win1");              //根據 id 得到 win 相應的 Element 
var dom=Ext .getDom("win1");        //得到 id 為 win1 的 DOM 節點

若要進一步了解 extjs 的 Class Diagram 可參考下列網址:Ext JS 3.0 - Class Diagram

深入 extjs

如果要想深入應用 Ext,那麼閱讀Ext項目的原始碼是必要的,該原始碼非常值的學習,一些 js 技巧,以及應該要避免的 js 陷阱都有考慮到,透過閱讀原始碼可以更加深刻的了解 javascript 的程式開發,其包含了很多精巧的 js 開發與設計模式。在使用Ext的過程中,需要 extend extjs 的元件作為自定義的元件時,如何實作有時就要從原始碼找答案。

當然 Ext 的原始碼是很龐大的,並不需要從頭開始看,建議可以從元件 Component.js、容器 Container.js 以及 Panel.js;而核心中的 Element.js 以及 Ext.js 等也是蠻重要的。

張貼留言