iT邦幫忙

DAY 4
1

extjs 4 + grails 開發實戰系列 第 4

extjs 精簡程式碼案例分享

在有限的時間中,要學會一個前端框架不容易,也沒有一個框架是萬能的,但總要有個開始,筆者在進幾年一直在 extjs 的世界打滾,已有三四年的經驗,最近負責的專案除了使用 extjs,後端使用 grails,發現好處多多,就開發過程中的經驗與大家分享,從實際的例子還有兩個框架遇到得整合問題一一介紹
程式的寫法百百種,要能夠完成一個特定功能沒有標準答案,但要寫的精簡需要經驗的累積,甚至需要對語言特性要有一定的了解,剛好筆者最近 review 了一段 extjs 程式碼,可以與大家分享。

在開始之前,必須先提到一個觀念,extjs 雖然是 RIA,並且有自己的類別化系統,不過他的類別或物件脫離不了 javascript,因此 javascript 中的物件函數this 的特性他也都有。

接著我們來看一段 extjs 類別的程式碼,如下:

這樣的結構可以參考 extjs 官方對於 Ext.define的說明,其中函數所包含的參數 define( className, data, createdFn ) data 的形態就是 Object,所以在下列程式第二個參數所傳入的就是標準的 javascript Object,只是裡面所定義的屬性 extjs 有自己的特定的命名以便辨別作為處理,但不代表你不可以把它當作一般的 Object 來操做,在使用 extjs 時很容易讓使用的人誤解認為他是個特別的存在,其實他也只是 javascript。

有了這樣的認知,我們看看下面的程式碼,裡面有很多段重覆的程式碼,參考註解。

Ext.define('foodprint.view.	', {
    extend: 'Ext.toolbar.Toolbar',
    alias: 'widget.stdeditortoolbar',

    requires: [
        'foodprint.view.CreateBtn',
        'foodprint.view.DeleteBtn',
        'foodprint.view.SaveBtn',
        'foodprint.view.ReadBtn',
        'foodprint.view.UpdateBtn'
    ],

    itemId: 'stdEditorToolbar',

    initComponent: function() {
        var me = this;

        Ext.applyIf(me, {
            items: [
                {
                    xtype: 'createbtn',
                    listeners: {
                        click: {
                            fn: me.onCreateBtnClick,
                            scope: me
                        }
                    }
                },
                {
                    xtype: 'deletebtn',
                    disabled: true,
                    listeners: {
                        click: {
                            fn: me.onDeleteBtnClick,
                            scope: me
                        }
                    }
                },
                {
                    xtype: 'savebtn',
                    disabled: true,
                    listeners: {
                        click: {
                            fn: me.onSaveBtnClick,
                            scope: me
                        }
                    }
                },
                {
                    xtype: 'readbtn'
                },
                {
                    xtype: 'updatebtn',
                    disabled: true,
                    listeners: {
                        click: {
                            fn: me.onUpdateBtnClick,
                            scope: me
                        }
                    }
                }
            ]
        });

        me.callParent(arguments);
    },

    onCreateBtnClick: function(button, e, eOpts) {
    		//重覆
        var crebtn=btn;
        var delbtn=button.ownerCt.down('button[itemId=deleteBtn]');
        var savbtn=button.ownerCt.down('button[itemId=saveBtn]');
        var updbtn=button.ownerCt.down('button[itemId=updateBtn]');
        var redbtn=button.ownerCt.down('button[itemId=readBtn]');

        delbtn.setDisabled(true);
        savbtn.setDisabled(false);
        updbtn.setDisabled(true);
        redbtn.setDisabled(true);
    },

    onDeleteBtnClick: function(button, e, eOpts) {
    		//重覆
        var crebtn=button.ownerCt.down('button[itemId=createBtn]');
        var delbtn=button;
        var savbtn=button.ownerCt.down('button[itemId=saveBtn]');
        var updbtn=button.ownerCt.down('button[itemId=updateBtn]');
        var redbtn=button.ownerCt.down('button[itemId=readBtn]');

        delbtn.setDisabled(true);
    },

    onSaveBtnClick: function(button, e, eOpts) {
    		//重覆
        var crebtn=button.ownerCt.down('button[itemId=createBtn]');
        var delbtn=button.ownerCt.down('button[itemId=deleteBtn]');
        var savbtn=button;
        var updbtn=button.ownerCt.down('button[itemId=updateBtn]');
        var redbtn=button.ownerCt.down('button[itemId=readBtn]');

        savbtn.setDisabled(true);
    },

    onUpdateBtnClick: function(button, e, eOpts) {
    		//重覆
        var crebtn=button.ownerCt.down('button[itemId=createBtn]');
        var delbtn=button.ownerCt.down('button[itemId=deleteBtn]');
        var savbtn=button.ownerCt.down('button[itemId=saveBtn]');
        var updbtn=button;
        var redbtn=button.ownerCt.down('button[itemId=readBtn]');

        updbtn.setDisabled(true);
    }

});

上面的每一個函式都重覆宣告了 5 個 btn,這樣在維護上是沒有效率的,假設我們需要在加一個按鈕,我們就必須每個函式在加一個宣告,其實還有更好的方式,假設你了解了物件、函式、還有 this 的特性,我們可以將這五個按鈕的參照存在第二個參數 data 下,透過例如:this.updateBtn=component.down('button[itemId=createBtn]'); 如下面程式碼中 onStdEditorToolbarRender,用到的事件 binding 是 extjs Component render 事件 元件實體化後觸動該事件進行處理,只要找到一個這樣只執行一次的進入點,將需要用到的元件存入 this 這個物件,之後我們就不需要重覆 query 物件的參照,也更加快處理速度,雖然這樣的調整可能效果不明顯,但累積起還是很可觀的,特別當你感覺到慢的時候,可能已經遍佈程式碼中,**好習慣要從日常養成**。

Ext.define('foodprint.view.StdEditorToolbar', {
    extend: 'Ext.toolbar.Toolbar',
    alias: 'widget.stdeditortoolbar',

    requires: [
        'foodprint.view.CreateBtn',
        'foodprint.view.DeleteBtn',
        'foodprint.view.SaveBtn',
        'foodprint.view.ReadBtn',
        'foodprint.view.UpdateBtn'
    ],

    itemId: 'stdEditorToolbar',

    initComponent: function() {
        var me = this;

        Ext.applyIf(me, {
            items: [
                {
                    xtype: 'createbtn',
                    listeners: {
                        click: {
                            fn: me.onCreateBtnClick,
                            scope: me
                        }
                    }
                },
                {
                    xtype: 'deletebtn',
                    disabled: true,
                    listeners: {
                        click: {
                            fn: me.onDeleteBtnClick,
                            scope: me
                        }
                    }
                },
                {
                    xtype: 'savebtn',
                    disabled: true,
                    listeners: {
                        click: {
                            fn: me.onSaveBtnClick,
                            scope: me
                        }
                    }
                },
                {
                    xtype: 'readbtn'
                },
                {
                    xtype: 'updatebtn',
                    disabled: true,
                    listeners: {
                        click: {
                            fn: me.onUpdateBtnClick,
                            scope: me
                        }
                    }
                }
            ],
            listeners: {
                render: {
                    fn: me.onStdEditorToolbarRender,
                    scope: me
                }
            }
        });

        me.callParent(arguments);
    },

    onCreateBtnClick: function(button, e, eOpts) {
        this.delbtn.setDisabled(true);
        this.savbtn.setDisabled(false);
        this.updbtn.setDisabled(true);
        this.redbtn.setDisabled(true);
    },

    onDeleteBtnClick: function(button, e, eOpts) {
        this.delbtn.setDisabled(true);
    },

    onSaveBtnClick: function(button, e, eOpts) {
        this.savbtn.setDisabled(true);
    },

    onUpdateBtnClick: function(button, e, eOpts) {
        this.updbtn.setDisabled(true);
    },

    onStdEditorToolbarRender: function(component, eOpts) {
        this.crebtn=component.down('button[itemId=createBtn]');
        this.delbtn=component.down('button[itemId=deleteBtn]');
        this.savbtn=component.down('button[itemId=saveBtn]');
        this.updbtn=component.down('button[itemId=updateBtn]');
        this.redbtn=component.down('button[itemId=readBtn]');

    }

});

優化過的程式碼,可以看到物件的 query 只有一次,之後都可以透過 this 來存取,每個函式所執行的都是關鍵的程式碼,沒有多餘的宣告,雖然是很簡單的觀念與應用,如果知道將會受用無窮,不只在 extjs 在一般的 javascript 開發也可以應用。

Ext JS 教學內容由思創軟體提供,共同作者 @lyhcode@smlsun 目前在校園及企業從事 JavaScript(含 Node.js, Ext JS)與 Java(含 Groovy, Grails, Gradle) 教育訓練及顧問工作。


上一篇
extjs 4 mixins(混和) 特性說明
下一篇
extjs 4:事件宣告的建議
系列文
extjs 4 + grails 開發實戰9
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言