内容


构建 Ext JS 扩展和插件

简介

Ext JS 是一个含有大量特性的综合的 JavaScript 框架,包括跨浏览器兼容的 JavaScript 帮助工具,用于 DOM 遍历和操作,以及数据对象存储。它也处理 Ajax 和直接远程 web 访问,涵盖了 UI 控件和小部件、图和表、以及一个功能强大的数据网格控件,还有很多很多...令人印象深刻。

当使用 Ext JS,或者需要 JavaScript 库或框架开发应用程序时,您可以确定您将需要一些功能,这些功能可能不是开箱即用的,或者不能以您想要的方式运行。谢天谢地,Ext JS 包含一个功能强大的类和组件生态系统,可以轻松地扩展现有特性或者完全构建新组件。在本文中,我们将探讨这个可扩展性,特别是扩展概念和插件。您将了解每个概念的含义以及它们之间的不同。然后,您将学习如何构建您自己的扩展和插件,以及如何追溯现有插件(其他开发人员构建的)的来源,将其用于您的应用程序中,这样就避免了重复性的工作。

什么是扩展?

Ext JS 的扩展是从类或者子类衍生出来的,为支持现有 Ext JS 类包含附加特性而设计的。在使用一个具体组件时,如果您经常需要提供这些参数,扩展基本上和 “预配置类” 一样有用,预配置类本质上是为一个已有的类配置提供一组默认值。例如,您的应用程序通过 Ext.Window 类可以使用一系列弹出窗口,而这些窗口的宽度和长度总是相同的。与其每次都使用 Ext.Window 指定长度和宽度值,不如创建一个含有预配置大小属性的 Ext.Window 扩展。这不仅使您避免了一遍又一遍的重复编写相同的代码,也使您的应用程序更易于维护。如果您需要修改您应用程序弹出窗口的默认大小,您只需要在您代码中的一个地方进行修改,不需要在几个不同位置修改。

预配置类是最基本的扩展类型。除了为类中现有属性提供值,子类也可以添加父类中不包含的新属性和方法(就是说,类是被子类化或者衍生的)

如果您对面向对象编程语言基本原理比较熟悉,您就知道当一个对象被实例化时,类构造函数被自动调用。该函数通常执行一些初始化例行程序,比如,设置类属性的默认值。默认情况下,当您创建一个子类时,它的构造函数只调用其父类的构造函数。然而,通过为子类创建一个构造函数,您可以重写父类构造函数,执行面向子类的功能(例如,设置被子类添加的默认值属性)。Ext 使这一操作非常简单,确保您可以轻松地从子类构造函数调用父类或超级类的构造函数 ,这样您不需要将父类构造函数的代码添加到子类构造函数。

父类中的重写方法不仅局限于构造函数。您也可以在子类中重写规则函数,从子类中调用重写的父类函数。

什么是插件?

插件是用于向已有组件提供附加功能的类。插件的使用并不是直接初始化插件类的一个对象,而是使用该组件的 “plugins” 配置选项将插件附加到这个组件上。使用插件的一个主要优势是,插件不仅可以被附加的组件所使用,也可以被所有从该组件衍生的子类所使用。

在 Ext JS 中使用组件的一个特点就是,多个插件可以被附加到一个组件上,根据需要提供附加功能。这意味着附加功能可以被分解,且仅在需要时使用,来提高应用程序的性能。当然,插件应该以一种与其他插件不冲突的方式写入,否则它们将不能在一个插件中同时使用。

扩展和插件之间的区别

首先,在 Ext JS 中很难分辨扩展和插件之间的不同,尤其是考虑到它们在使用时的相似性。无论如何,它们都向 Ext JS 类提供扩展功能。主要不同之处是它们的编写方式。

Ext JS 扩展是作为一个现有类的子类编写的,它们可以提供附加属性或函数,或者甚至可以通过重写构造函数和其他函数来修改父类的行为。要使用一个扩展,您应该直接创建一个扩展子类对象,然后使用、添加、或者重写特性到父类(它从其中衍生的)。扩展最好在您需要改变一个已有 Ext JS 功能或特性的核心功能时,或者在您想要构建一个全新的组件时使用。

Ext JS 插件是作为类编写的,总是有一个 init 函数。不像扩展,您不需要直接创建一个插件类对象,但是您要使用组件(它所附加的)来注册您的插件。然后,在插件中定义的选项和函数将可被组件本身所用。插件最好在您需要向一个组件添加特性时使用。因为扩展同它的所衍生的父类是紧密耦合的,当您想要您的附加物易于分离以及多个组件和衍生组件同步操作时,插件也提出了一个可行的备用方案。插件必须被附加在一个现有组件中,因为它通常不适合从零开始构建新组件。

构建一个 Ext JS 扩展

在这一小节,您将学习如何构建 Ext JS 扩展。首先您将学习如何创建预配置的类,通过创建将配置选项预设为默认值的子类来使您的代码易于重用,而且使您的计算机保持在一个可维护的状态。然后,您将学习如何创建一个更复杂的扩展,可以修改一个已有 Ext JS 类的功能以及添加一些新特性。

创建预配置类

正如上文所述,预配置类是 Ext JS 类最基本的形式。它们简单定义了一个预配置选项值集合,允许您使用一个组件,不需要每次都传递默认值。其中一个示例是 Ext.Window 组件,在您的应用程序中很多窗口属性都是相同的,例如,宽度、高度和标题。举个这样的例子,看这个具体场景,不使用一个预配置类(见 清单 1)。

清单 1. unextended.html
<html>
    <head>
        <title>Popup Window - Unextended Version</title>
        <link rel="stylesheet" type="text/css" href="ext/resources/
css/ext-all.css" />
        <script type="text/javascript" src="ext/adapter/ext/
ext-base.js"></script>
        <script type="text/javascript" src="ext/ext-all.js"></script>
        <script type="text/javascript">
        Ext.onReady(function() {
            var win1 = new Ext.Window({
                width: 500,
                height: 300,
                modal: true,
                closeAction: 'hide',
                resizable: false,
                maximizable: true,
                collapsible: true,
                draggable: false,
                title: 'Window 1',
                html: '<h2>Window 1</h2>'
            });

            var win2 = new Ext.Window({
                width: 500,
                height: 300,
                modal: true,
                closeAction: 'hide',
                resizable: false,
                maximizable: true,
                collapsible: true,
                draggable: false,
                title: 'Window 2',
                html: '<h2>Window 2</h2>'
            });

            var win3 = new Ext.Window({
                width: 500,
                height: 300,
                modal: true,
                closeAction: 'hide',
                resizable: false,
                maximizable: true,
                collapsible: true,
                draggable: false,
                title: 'Window 3',
                html: '<h2>Window 3</h2>'
            });

            var button1 = Ext.get('button1');
            var button2 = Ext.get('button2');
            var button3 = Ext.get('button3');

            button1.on('click', function() {
                win1.show(this);
            });

            button2.on('click', function() {
                win2.show(this);
            });

            button3.on('click', function() {
                win3.show(this);
            });
        });
        </script>
    </head>

    <body>
        <button id="button1">Show Window 1</button>
        <br />
        <button id="button2">Show Window 2</button>
        <br />
        <button id="button3">Show Window 3</button>
    </body>
</html>

清单 1 中有 3 个按钮,每一个将显示一个独立的 Ext.Window 组件,对于下列属性,每一个窗口值都是相同的:

  • width
  • height
  • modal
  • closeAction
  • resizable
  • maximizable
  • collapsible
  • draggable

在清单 1 中,您可以清楚的看到在配置中要为 3 个 Ext.Window 对象的每一个都设置这个 8 个属性。这样做不仅会产生多行代码,而且当您决定修改 Ext.Window 的这些默认属性值时,需要在 3 个不同的地方进行修改,不是很理想。在这一方面预配置类可以发挥作用,如 清单 2 所示。

清单 2. extended.html
<html>
    <head>
        <title>Popup Window - Extended Version</title>
        <link rel="stylesheet" type="text/css" href="ext/resources/css/ext-all.css" />
        <script type="text/javascript" src="ext/adapter/ext/ext-base.js"></script>
        <script type="text/javascript" src="ext/ext-all.js"></script>
        <script type="text/javascript">
        MyWindow = Ext.extend(Ext.Window, {
            width: 500,
            height: 300,
            modal: true,
            closeAction: 'hide',
            resizable: false,
            maximizable: true,
            collapsible: true,
            draggable: false
        });

        Ext.onReady(function() {
            var win1 = new MyWindow({
                title: 'Window 1',
                html: '<h2>Window 1</h2>'
            });

            var win2 = new MyWindow({
                title: 'Window 2',
                html: '<h2>Window 2</h2>'
            });

            var win3 = new MyWindow({
                title: 'Window 3',
                html: '<h2>Window 3</h2>'
            });

            var button1 = Ext.get('button1');
            var button2 = Ext.get('button2');
            var button3 = Ext.get('button3');

            button1.on('click', function() {
                win1.show(this);
            });

            button2.on('click', function() {
                win2.show(this);
            });

            button3.on('click', function() {
                win3.show(this);
            });
        });
        </script>
    </head>

    <body>
        <button id="button1">Show Window 1</button>
        <br />
        <button id="button2">Show Window 2</button>
        <br />
        <button id="button3">Show Window 3</button>
    </body>
</html>

清单中的很多代码和 清单 1 中的完全一样。然而,您会注意到 Ext.onReady 调用,我创建了一个新子类 MyWindow。这是使用 Ext.extend 函数创建的,指定在 3 个 Ext.Window 对象中普遍存在的 8 个属性的默认值。正如您所看到的,您可以创建这个新子类的对象,指定两个在每个窗口中各不相同的属性(titlehtml)。两个清单中的代码生成的应用程序看上去完全一样(见 图 1),但是 清单 2 是一个更容易维护的方法,我相信您也同意这一点。

图 1. 运行中的预配置类示例
 Mozilla Firefox 中 MessageBox 的屏幕截图,显示 Window 1
Mozilla Firefox 中 MessageBox 的屏幕截图,显示 Window 1

扩展一个 Ext JS 组件

为组件预定义配置选项当然好,但是如何扩展组件的功能以提供其他特性呢?在这一小节,您将学习如何创建一个具有 web 浏览器窗口性质的 Ext.Window 子类。

您可以使用 <iframe> HTML 元素来在另一个 web 页面中显示 web 页面,我们正好利用这一优势来创建一个新 ExtBrowserWindow 类,该类可以在 Ext.Window 中显示这个 web 页面。目标是简化处理过程。要创建一个弹出的 Ext 浏览器窗口,您所要做的只是提供一个惟一 ID 和一个 URL 来显示。事实上,在这个示例中,您只需提供一个默认 URL 来导航,除此之外不需要提供什么。

清单 3 展示了该示例的完整源代码。

清单 3. extbrowser.html
<html>
    <head>
        <title>Ext Browser Window Extension</title>
        <link rel="stylesheet" type="text/css" 
href="ext/resources/css/ext-all.css" />
        <script type="text/javascript" src="ext/adapter/ext
/ext-base.js"></script>
        <script type="text/javascript" src="ext/ext-all.js"></script>
        <script type="text/javascript">
        ExtBrowserWindow = Ext.extend(Ext.Window, {
            closeAction: 'hide',
            width: 500,
            height: 300,
            title: 'Sencha',
            url: 'http://www.sencha.com',
            onRender: function() {
                this.bodyCfg = {
                    tag: 'iframe',
                    src: this.url,
                    cls: this.bodyCls,
                    style: { border: '0px none' }
                };
                ExtBrowserWindow.superclass.onRender.apply(this, arguments);
            }
        });

        Ext.onReady(function() {
            var win1, win2;

            var button1 = Ext.get('button1');
            var button2 = Ext.get('button2');

            button1.on('click', function() {
                if(!win1) {
                    win1 = new ExtBrowserWindow({
                        id: '1',
                        title: 'Google',
                        url: 'http://www.google.com'
                    });
                }
                win1.show(this);
            });

            button2.on('click', function() {
                if(!win2) {
                    win2 = new ExtBrowserWindow({
                        id: '2',
                    });
                }
                win2.show(this);
            });
        });
        </script>
    </head>

    <body>
        <button id="button1">Show Google</button>
        <br />
        <button id="button2">Show Sencha</button>
    </body>
</html>

类似于预配置类示例,您创建了 Ext.Window 类的一个子类并提供了一些默认配置选项,比如,宽度、高度和标题。然而,在该实例中,您也设置了一个指向这个新类的新选项,url。该属性确定将要在窗口中显示的 web 页面 URL。在示例中,默认设置为 Sencha 主页面(见 参考资料)。

ExtBrowserWindow 子类也重写 onRender 事件函数,并将一个配置应用到 Ext.Window 之中,通知它应该使用 iframe 标签,iframe 标签的 src 属性应被设置为 url 配置选项、style 设置为无边框。最后,超类中相应的 onRender 事件处理程序将被调用。

实际上,ExtBrowserWindow 对象的实例化和前面的示例一样,只是这次我们需要传递我们的第一个按钮调用(将打开 Google)中的用户定义配置选项 url。在第 2 个示例中,我们只使用在类中指定的默认值,这将显示 Sencha 主页面。图 2 显示了实际运行的 ExtBrowserWindow 扩展。

图 2. 实际运行的 ExtBrowserWindow 扩展
Mozilla Firefox 中 Ext Browser Window 扩展的屏幕截图,在新窗口中显示 Sencha 首页
Mozilla Firefox 中 Ext Browser Window 扩展的屏幕截图,在新窗口中显示 Sencha 首页

这是 Ext JS 扩展的一个最基础的示例,但是它很容易进一步扩展来包括一些更多的特性。例如,您可以添加典型的 web 浏览器功能,比如,前进和后退按钮、一个地址栏、或者书签。

构建一个 Ext JS 插件

在本小节,我们将学习如何构建一个 Ext JS 插件。为了简便起见,我将为您介绍如何创建同样的功能(与上一节相同),但是这次是作为一个插件创建,而不是一个扩展。在一个外部 JavaScript 源文件中创建一个插件是一种惯例,因此,先创建一个新文件 BrowserPlugin.js,其内容如 清单 4 所示。

清单 4. BrowserPlugin.js
BrowserPlugin = Ext.extend(Object, {    
    init: function(component) {        
        component.width = 500;
        component.height = 300;
        component.bodyCfg = {
            tag: 'iframe',
            src: component.url,
            cls: component.bodyCls,
            style: { border: '0px none' }
        };
    }
});
Ext.preg('browserPlugin', BrowserPlugin);

这个插件非常简单:包含一个 Ext JS 插件的基本需求。init 函数相应地设置组件的宽度和高度,然后通知组件应该以一个 <iframe> 元素显示,src 属性来自 URL 配置选项,和扩展示例一样。最后,BrowserPlugin 类作为一个插件被注册,允许被懒加载。这基本上意味着您不需要人工初始化对象;Ext JS 将为您处理。

接着,创建 plugin.html,这个文件将使用您刚刚创建的插件。具体内容在 清单 5 中列出。

清单 5. plugin.html
<html>
    <head>
        <title>Ext Browser Window Extension</title>
        <link rel="stylesheet" type="text/css" href="ext/resources/css
/ext-all.css" />
        <script type="text/javascript" src="ext/adapter/ext
/ext-base.js"></script>
        <script type="text/javascript" src="ext/ext-all.js"></script>
        <script type="text/javascript" src="BrowserPlugin.js"></script>
        <script type="text/javascript">
        Ext.onReady(function() {
            var win1, win2;

            var button1 = Ext.get('button1');
            var button2 = Ext.get('button2');

            button1.on('click', function() {
                if(!win1) {
                    win1 = new Ext.Window({
                        title: 'Google',
                        closeAction: 'hide',
                        plugins: ['browserPlugin'],
                        url: 'http://www.google.ie'
                    });
                }
                win1.show(this);
            });

            button2.on('click', function() {
                if(!win2) {
                    win2 = new Ext.Window({
                        title: 'Sencha',
                        closeAction: 'hide',
                        plugins: ['browserPlugin'],
                        url: 'http://www.sencha.com'
                    });
                }
                win2.show(this);
            });
        });
        </script>
    </head>

    <body>
        <button id="button1">Show Google</button>
        <br />
        <button id="button2">Show Sencha</button>
    </body>
</html>

再一次,这个代码非常熟悉。您将注意到加载了 ext-all.js 之后新的 BrowserPlugin.js 文件也被加载。扩展和插件使用的主要区别在单击两个按钮的事件处理程序时可以看到。当我们创建了一个扩展时,我们初始化新扩展的子类(ExtBrowserWindow)的一个对象,但是如果使用一个插件时,您使用的是您想要显示的规则 Ext JS 组件。在这个组件的配置选项中,您可以使用 “plugins” 配置选项指定您想要加载的 “browserPlugin” 插件。

这同我们在上一小节创建的扩展的结果是一样的,在 图 2 中您可以看到它的屏幕截图,如前所述。在这一小节中,我们将介绍创建 Ext JS 插件的基础。可以这样说,关于这一主题的文档和阅读材料是相当的少,因此学习更先进的技术的最好方法是下载、使用、和随意修改 Ext JS 社区中其他用户创建的插件和扩展。下一节,您将学习如何在您的应用程序中使用现有的扩展。

使用已有 Ext JS 插件

找到现有 Ext JS 扩展和插件有很多种方法。第一个(也是最完整的)来源是 Sencha 网站(见 参考资料)上的 Ext User Extensions 和 Plug-ins 论坛。还有一个非官方用户扩展库(见 参考资料)。多数插件和扩展只是一个 JavaScript 源文件,您可以下载和复制,在您自己的应用程序中使用。这些插件和扩展的使用许可比较宽松,大多数都是免费使用的,但是在一个商业项目中使用之前一定要检查许可证的使用问题。

如果要包括一个已有的扩展或插件,您应该确保在包含了 Ext JS 的源 JavaScript 和 CSS 文件之后再这样做。扩展和插件通常会重写默认 Ext JS 组件功能,因此要求在加载它们之前加载这些组件。

这个 Ext JS 项目示例包括一系列正在运行的定制扩展的演示,在演示中,查看页面的 HTML 源文件,寻找扩展或者插件 JavaScript 源文件的 URL(如果您使用的是 JavaScript,您可以点击链接直接打开源文件)。复制这些代码,粘贴到一个文件中,在您的项目中使用。每个演示都有一个 JavaScript 代码链接,实际上是将这些插件付诸实践,故单击链接观看示例如何运行。

参考资料 中获取 Ext JS 样例的链接,这些样例是构建在用户扩展之上的。

正如您所看到的,大量官方 Ext JS 样例和演示都是建立在用户共享的扩展之上的。用户社区在增强 Ext JS 框架中起到很重要的作用,甚至有几个用户扩展已经变成框架本身的标准组件了。

花一点时间来研究以上演示、看看扩展源代码,获取对扩展和插件的复杂程度的一个正确了解。用户在 Ext JS 中的扩展通常是在名称空间 Ext.ux 中,因此在您试着寻找一个扩展的源代码时要留意这一点。

结束语

在本文中,您学习了 Ext JS 扩展和插件开发的基本理论。您也了解了这两个概念的含义、它们之间的不同、以及如何实际创建它们。您创建了一个预配置类,可以节省时间和宝贵的代码行,使应用程序更为高效、更易维护。然后您学习了如何创建一个 Ext JS 扩展 — 子类化 Ext.Window 组件的一个浏览器窗口。您也学习了如何定义新的属性,在子类中调用相应函数,以及如何在您的应用程序代码中使用这个新类。然后,您还学习了如何创建插件,定义强制的 init 函数,以及如何注册插件使其可用于 Ext JS 组件。在本文中学习了这些基础理论之后,您就可以开始学习一些更复杂的 Ext JS 扩展和插件开发的概念了。


下载资源


相关主题

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Web development
ArticleID=773371
ArticleTitle=构建 Ext JS 扩展和插件
publish-date=11092011