EXT4 在ExtJS4應用中使用Ext.Loader


         

在EXTJS官網看到一片文章,講的是Extjs.Loader的使用方法,文章非常詳細的介紹了Loader的機制及用法,感覺非常不錯,但無奈 英文實在太爛,就沒轉過來。昨天恰好在CSDN看到了這篇文章的譯文,而譯文的質量非常高,對譯者的翻譯水平怎一個羡慕了得。廢話不多說,正文開始。

在開始之前,將英文原文鏈接放上來,英文水平高的可以看看原文哦。

鏈接地址:www.sencha.com/blog/using-ext-loader-for-your-application/

ExtJS 4.0是一個使用新的依賴系統的類加載系統。這兩個強大的新功能允許你創建大量允許瀏覽器按需下載腳本代碼的應用。

今天,我們將通過建立一個小的使用新的類加載系統的應用程序來熟悉一下依賴管理系統。同時,我們將討論Ext加載系統的各種配置項。

在開始之前,我們先來看看將要實現的結果。這樣做,可使我們確定需要擴展那些類。


應用會包括互相綁定的GridPanel和FormPanel,名稱分別為UserGridPanel和UserFormPanel。 UserGridPanel的操作需要創建一個模型和Store。UserGridPanel和UserFormPanel將被渲染到一個名稱為 UserEditorWindow的窗口,它擴張自ExtJS的Window類。所有這些類都會在命名空間MyApp下。

在開始編碼前,首先要確定目錄結構,以下是使用命名空間組織的文件夾:


從上圖可以看到,MyApp目錄已經按照命名空間進拆分成幾個目錄。在完成開發的時候,我們的應用將會有一個如下圖所示的內部依賴運行模型。

盡管應用的目錄構成很象ExtJS 4 MVC架構,事實上示例並沒有使用它


現在開始編寫index.html文件,這里需要包含應用需要的啟動文件和應用的根文件(app.js)。

 
<!DOCTYPE HTML PUBLIC  "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
    <title>Ext 4 Loader</title>
    <link rel="stylesheet" type="text/css"  href="js/ext-4.0.1/resources/css/ext-all.css" />
    <script type="text/javascript" src="js/ext-4.0.1/ext-debug.js"></script>
    <script type="text/javascript" src="js/MyApp/app.js"></script>
</head>
<body>
</body>
</html>

index.html文件中需要使用link標記包含ExtJS 4的樣式文件。包含ext-debug.js文件的javascript標記可能要修改多次,ext-all-debug.js文件是開發調試用的,而ext-all.js則是在發布產品時使用的。

這里有幾個選擇,每個選擇都有優缺點。

以下是這些文件的說明:

ext-all-debug-w-comments.js:帶注釋的的完整調試版本。文件比較大,加載時間比較長。

ext-all-debug.js : 不帶注釋的完整調試版本。文件也比較大,但很適合調試。

ext-all.js ;壓縮后的完整版本,文件比較小。使用該版本調試很困難,因此一般在發布產品時才使用。

ext-debug.js : 該文件只包含ExtJS基礎架構和空的結構。使用該文件,可以實現ExtJS類文件的遠程加載,而且提供了很好的調試體驗,不過代價是相當的慢。

ext.js : ext-debug.js的壓縮版本。

我們的index.html將使用ext-debug.js文件,這是實現動態加載所需的最低要求。最后,我們將展示如何使用ext-all版本獲取最好的結果。

由於UserGridPanel 類要求模型和Store,因而,要先定義編寫這些支持類。現在開始編寫模型和Store:

 
Ext.define('MyApp.models.UserModel', {
    extend   : 'Ext.data.Model',
    fields   : [
        'firstName',
        'lastName',
        'dob',
        'userName'
    ]
});

以上代碼擴展自Ext.data.Model,將創建UserModel 類。因為擴展自Ext.data.Model類,ExtJS會自動加載它,並在它加載后創建UserModel類。

下一步,要創建擴展自Ext.data.Store的UserStore 類:

 
Ext.define('MyApp.stores.UserStore', {
    extend    : 'Ext.data.Store',
    singleton : true,
    requires  : ['MyApp.models.UserModel'],
    model     : 'MyApp.models.UserModel',
    constructor : function() {
        this.callParent(arguments);
        this.loadData([
            {
                firstName : 'Louis',
                lastName  : 'Dobbs',
                dob       : '12/21/34',
                userName  : 'ldobbs'
            },
            {
                firstName : 'Sam',
                lastName  : 'Hart',
                dob       : '03/23/54',
                userName  : 'shart'
            },
            {
                firstName : 'Nancy',
                lastName  : 'Garcia',
                dob       : '01/18/24',
                userName  : 'ngarcia'
            }
        ]);
    }
});

當創建單件模式的UserStore 時,需要在UserStore原型添加一個requires關鍵字,它會在類實例化前,為ExtJS提供一個類的請求列表。在這個示例,列表中只有UserModel 一個請求類。

(實際上, 在Store的原型中定義了model為UserModel 類,ExtJS就會自動加載它。在requires關鍵字中列出的目的,是希望你的代碼能自文檔化(self-documenting),從而提醒你,UserModel 類是必須的 )

好了,UserGridPanel視圖需要的基類已經創建了,現在可以創建UserGridPanel類了:

 
Ext.define('MyApp.views.UsersGridPanel', {
    extend   : 'Ext.grid.Panel',
    alias    : 'widget.UsersGridPanel',
    requires : ['MyApp.stores.UserStore'],
    initComponent : function() {
        this.store   = MyApp.stores.UserStore;
        this.columns = this.buildColumns();
        this.callParent();
    },
    buildColumns : function() {
        return [
            {
                header    : 'First Name',
                dataIndex : 'firstName',
                width     : 70
            },
            {
                header    : 'Last Name',
                dataIndex : 'lastName',
                width     : 70
            },
            {
                header    : 'DOB',
                dataIndex : 'dob',
                width     : 70
            },
            {
                header    : 'Login',
                dataIndex : 'userName',
                width     : 70
            }
        ];
    }
});

 

在上面代碼中,要注意requires 關鍵字,看它是怎么增加UserStore 為請求類的。剛才,我們為GridPanel擴展和Store擴展配置了一個直接的依賴關系。

下一步,我們要創建FormPanel擴展:

 
Ext.define('MyApp.views.UserFormPanel', {
    extend      : 'Ext.form.Panel',
    alias       : 'widget.UserFormPanel',
    bodyStyle   : 'padding: 10px; background-color: #DCE5F0;' 
            + ' border-left: none;',
    defaultType : 'textfield',
    defaults    : {
        anchor     : '-10',
        labelWidth : 70
    },
    initComponent : function() {
        this.items = this.buildItems();
        this.callParent();
    },
    buildItems : function() {
        return [
            {
                fieldLabel : 'First Name',
                name       : 'firstName'
            },
            {
                fieldLabel : 'Last Name',
                name       : 'lastName'
            },
            {
                fieldLabel : 'DOB',
                name       : 'dob'
            },
            {
                fieldLabel : 'User Name',
                name       : 'userName'
            }
        ];
    }
});

 

因為UserForm 不需要從服務器端請求任何類,因而不需要添加requires定義。

應用快完成了,現在需要創建UserEditorWindow類和運行應用的app.js。以下是UserEditorWindow類的代碼。因為要將Grid和表單綁定在一起,因而類代碼有點長,請見諒:

 
Ext.define('MyApp.views.UserEditorWindow', {
    extend   : 'Ext.Window',
    requires : ['MyApp.views.UsersGridPanel','MyApp.views.UserFormPanel'],
    height : 200,
    width  : 550,
    border : false,
    layout : {
        type  : 'hbox',
        align : 'stretch'
    },
    initComponent : function() {
        this.items   = this.buildItems();
        this.buttons = this.buildButtons();
        this.callParent();
        this.on('afterrender', this.onAfterRenderLoadForm, this);
    },
    buildItems : function() {
        return [
            {
                xtype     : 'UsersGridPanel',
                width     : 280,
                itemId    : 'userGrid',
                listeners : {
                    scope     : this,
                    itemclick : this.onGridItemClick
                }
            },
            {
                xtype  : 'UserFormPanel',
                itemId : 'userForm',
                flex   : 1
            }
        ];
    },
    buildButtons : function() {
        return [
            {
                text    : 'Save',
                scope   : this,
                handler : this.onSaveBtn
            },
            {
                text    : 'New',
                scope   : this,
                handler : this.onNewBtn
            }
        ];
    },
    onGridItemClick : function(view, record) {
        var formPanel = this.getComponent('userForm');
        formPanel.loadRecord(record)
    },
    onSaveBtn : function() {
        var gridPanel  = this.getComponent('userGrid'),
            gridStore  = gridPanel.getStore(),
            formPanel  = this.getComponent('userForm'),
            basicForm  = formPanel.getForm(),
            currentRec = basicForm.getRecord(),
            formData   = basicForm.getValues(),
            storeIndex = gridStore.indexOf(currentRec),
            key;
        //loop through the record and set values
        currentRec.beginEdit();
        for (key in formData) {
            currentRec.set(key, formData[key]);
        }
        currentRec.endEdit();
        currentRec.commit();
        // Add and select
        if (storeIndex == -1) {
            gridStore.add(currentRec);
            gridPanel.getSelectionModel().select(currentRec)
        }
    },
    onNewBtn : function() {
        var gridPanel = this.getComponent('userGrid'),
            formPanel = this.getComponent('userForm'),
            newModel  = Ext.ModelManager.create({}, 
                              'MyApp.models.UserModel');
        gridPanel.getSelectionModel().clearSelections();
        formPanel.getForm().loadRecord(newModel)
    },
    onAfterRenderLoadForm : function() {
        this.onNewBtn();
    }
});

 

UserEditorWindow 的代碼包含了許多東西用來管理UserGridPanel和UserFormPanel類的整個綁定的聲明周期。為了指示ExtJS在創建該類前加載這兩個類,必須在requires列表里列出它們。

現在完成最后一個文件app.js。為了最大限度地提高我們的學習,將有3次修改要做。首先從最簡單配置開始,然后逐步添加。

 
Ext.Loader.setPath('MyApp', 'js/MyApp');
Ext.onReady(function() {
    Ext.create('MyApp.views.UserEditorWindow').show();
});

 

首先,app.js會在ExtJS添加MyApp命名空間的路徑,這可通過調用Ext.loader.setPath方法實現,方法的第1個參數是命名空間,然后是加載文件與頁面的相對路徑。

下一步,調用Ext.OnReady方法,傳遞一個包含Ext.create的匿名函數。Ext.create會在ExtJS 4.0初始化之后執行,以字符串形式傳遞的UserEditorWindow 類會被實例化。因為不需要指向實例和希望立即顯示它,因而在后面串接了show方法的調用。

如果你打開這個頁面(http://moduscreate.com/senchaarticles/01/pass1.html ),你會看到UI渲染,但很慢,並且ExtJS會在Firebug中顯示以下警告信息:

004

ExtJS提示我們沒有使用加載系統最優化的方式。這是第二步要討論的問題。然后,這是一個好的學習機會,要好好理由。

     我們需要配置Firebug在控制台中顯示XHR請求,以便在控制台中看到所有請求,而不需要切換到網絡面板。這樣,我們不單可以觀察到類依賴系統的工作情況,還可以從所有ExtJS類加載的文件中通過過濾方式找到我們要求這樣的文件。

     在Firebug控制台過濾輸入框中輸入“User”,你會看到下圖所示的結果。

從圖中可以看到,UserEditorWindow類第一個被加載,接着請求UserGridPanel。UserGridPanel 要求UserStore和UserModel類。最后加載UserFormPanel 類。

我剛才提到,ExtJS提示了我們沒有使用加載系統最優化的方式。這是因為依賴是在Ext.OnReady觸發加載之后通過同步XHR請求確定的,而這不是有效的方式且難於調試。

未來修正這個問題,可以修改app.js指示ExtJS先加載我們定義的類,這樣即可提供性能又便於調試:

 
Ext.Loader.setPath('MyApp', 'js/MyApp');
Ext.require('MyApp.views.UserEditorWindow');
Ext.onReady(function() {
    Ext.create('MyApp.views.UserEditorWindow').show();
});

為了快速加載我們定義的類和避免調試信息,可簡單的在Ext.onReady前調用Ext.require,只是ExtJS請求 UserEditorWindow類。這將會讓ExtJS在文檔HEAD標記內注入一個script標記,運行資源在Ext.OnReady前加載。

查看http://moduscreate.com/senchaarticles/01/pass2.html 可看到它是如何工作地。在頁面加載后,你會注意到ExtJS沒有在控制台顯示警告信息了。

我們所做的是讓ExtJS框架和應用類延遲加載。雖然這樣做調試很好,但是對於需要快速調試的情況,頁面渲染時間會讓你感到痛苦。為什么?

原因很簡單,因為這需要加載許多資源文件。在示例中,ExtJS發送了193個Javascript資源請求到web服務器,還有部分是在緩存中的:


我們創建了6個Javascript文件(5個類文件和app.js),這意味着加載要求的ExtJS文件有187個請求。當你在本地做開發的時候,這個方案可行,但不是最理想的和效果最好的。

解決這個問題,我們可以使用折中方案,通過ext-all-debug加載ExtJS框架,動態加載我們的類文件。要實現這個,需要修改兩個文件。

<script type="text/javascript"  src="js/ext-4.0.1/ext-all-debug.js"></script>

首先,需要修改Index.html,使用ext-all-debug.js替換ext.debug.js。

接着,修改app.js,開啟Ext.Loader:

 
(function() {
    Ext.Loader.setConfig({
        enabled : true,
        paths   : {
            MyApp : 'js/MyApp'
        } 
    });
 
    Ext.require('MyApp.views.UserEditorWindow');
 
    Ext.onReady(function() {
        Ext.create('MyApp.views.UserEditorWindow').show();
    });
})();

通過調用Loader.setConfig可開啟Ext.Loader,需要傳遞一個匿名對象,它的eanbled屬性設置為true,而命名空間設置為路徑映射。

通過編輯app.js,在本地開發環境下,應用將會在1秒內完成加載和渲染。


源代碼下載地址:http://moduscreate.com/senchaarticles/01/files.zip


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM