原文:Understanding Widgets in Ext JS 5

在Ext JS 5,引入了新的“widgetcolumn”,支持在网格的单元格中放置组件。同时,还在Ext JS 5引入了一种新的被称为“小部件”的轻量级组件。在Ext JS 5中,已包含了几个小部件,在本文将告诉你如何轻松的去打造自己的小部件。

为了说明其中的关键概念,在文中将创建一个简单的名为“ratings”的如下图所示的小部件:

入门

与常用的从Ext.Component派生的组件不同,小部件派生于新的基类Ext.Widget。而且,Ext.Widget的派生类几乎完全是由配置系统来定义的(后面会谈到)。Ext.Widget还要定义了DOM元素是如何产生且如何与DOM事件连接的。

渲染

对于小部件,首先要考虑的是如何去定义它的DOM树,典型的方式是如以下代码哪样在类指定element属性来实现:

Ext.define('Ext.ux.rating.Picker', {
extend: 'Ext.Widget', //... element: {
cls: 'ux-rating-picker',
reference: 'element', children: [{
reference: 'innerEl',
cls: 'ux-rating-picker-inner', listeners: {
click: 'onClick',
mousemove: 'onMouseMove',
mouseenter: 'onMouseEnter',
mouseleave: 'onMouseLeave'
}, children: [{
reference: 'valueEl',
cls: 'ux-rating-picker-value'
},{
reference: 'trackerEl',
cls: 'ux-rating-picker-tracker'
}]
}]
}, //...
});

对象element是基于Ext.dom.Helper规范来创建DOM元素的,主要的新增功能是reference和listeners属性。这些名字在视图控制器上已经司空见惯了,而他们在小部件上所要做的事情也类似。在Ext.Widget,所有元素的reference属性都会以名称作为属性值被缓存在小部件实例中(例如:element、innerEl等等)。

事件

在上述的element中,在innerEl对象中还定义了listeners对象。这些监听会附加到从规范块产生的元素上。方法会根据名称在小部件类中寻找,例如:

Ext.define('Ext.ux.rating.Picker', {
extend: 'Ext.Widget', //... onClick: function (event) {
var value = this.valueFromEvent(event);
this.setValue(value);
}, onMouseEnter: function () {
this.element.addCls(this.overCls);
}, onMouseLeave: function () {
this.element.removeCls(this.overCls);
}, onMouseMove: function (event) {
var value = this.valueFromEvent(event);
this.setTrackingValue(value);
},

虽然这看起来有点类似编写传统的组件类,但比较明显的是缺少初始化代码和清理代码。Ext.Widget的构造函数会处理元素的创建、跟踪他们的引用并设置监听。除了这些动作(和相应的销毁方法)外,Ext.Widget再没有额外的生命周期或相关开销。

作为替代方法,派生类可通过配置系统提供的config属性来定义它的行为。对于那些不了解配置系统的,下面将简单介绍一下。

配置系统101

Ext JS的核心理念之一就是“配置(config)”属性的概念。他们从一开始就是Ext JS的一部分,而不单是在Ext JS 5(或Sencha Touch 2.x)才有,框架已经将这些属性的机制规范化。常见的配置是这样声明的:

Ext.define('Ext.ux.rating.Picker', {
//...
config: {
family: 'monospace'
}
//...
});

上述声明等同于以下的手写代码:

Ext.define('Ext.ux.rating.Picker', {
//... getFamily: function () {
return this._family;
}, setFamily: function (newValue) {
var oldValue = this._family; if (this.applyTitle) {
newValue = this.applyFamily(newValue, oldValue); // #1
if (newValue === undefined) {
return this;
}
} if (newValue !== oldValue) {
this._family = newValue; if (this.updateFamily) {
this.updateFamily(newValue, oldValue); // #2
}
} return this;
}, //...
});

这种自动化处理主要有以下显著的优点:

  • 清晰:更少的代码,类更易读
  • 一致性:所有配置都有相同的行为
  • 灵活性:当实现正确时,配置属性可以在任何时候改变,而不是只能在创建时改变(在Ext JS中许多旧的配置属性的常见的局限)

开发人员可以为任何属性提供两个关键的,也是可选的方法,如family,则为applyFamily和updateFamily(上面代码中的#1和#2处)。这些方法几乎总是会比重写而不是get或set方法。

应用方法(applier)

应用方法运行开发人员去将接收值转换为存储的实际值。对于许多应用方法来说,这可能意味着基于接收到的配置对象来创建一些类的实例,又或者可能是应用方法在有一个地方标准化内部表示形式以避免在所有使用该属性的地方对它进行检查。

更新方法(updater)

当配置属性的值发生了改变,就会调用更新方法。更新方法的作用就是将旧值转换为新值。

initConfig——把它捎上

最后要说的是,要将一个类加入熬配置系统中,就必须在某个点调用initConfig方法。在Ext.Widget,它会在构造函数中执行。方法initConfig会接收config对象并会处理它的每一个属性以便这些在类中的声明能适当的去调用set、apply和update方法。

该方法还提供了一个“适时(just in time)”的设置机制来解决配置属性之间的顺序问题,例如,如果一个配置属性的更新方法需要另一个配置属性的值,它就要调用另一个配置属性的get方法。在底层,initConfig会根据所请求属性之前返回的结果正确按照顺序调用set/apply/update方法。

使用cachedConfig进行优化

对于小部件,在有些时候需要许多配置来维护DOM。由于任何给定的小部件实例不太可能重写所有的默认配置值,如果可以将这些要处理的默认值缓存起来,是最理想不过了。对于这些配置,可以对类进行以下修改:

Ext.define('Ext.panel.Panel', {
//...
cachedConfig: {
family: 'monospace'
}
//...
});

在大多数情况,这些配置与常用的配置是一样的。不过,在缓存这些配置的时候,配置系统会在类创建第一个实例的时候执行一些额外处理。

第一次实例化

在第一个实例的配置对象处理之前,配置系统只会从类的默认值进行初始化。该处理会调用各种apply和update方法,这反过来会根据元素规范更新DOM元素最初的生成。

考虑下family配置,它带有以下更新方法:

updateFamily: function (family) {
this.element.setStyle('fontFamily', "'" + family + "'");
},

所有的更新方法有助于为小部件设置DOM的默认状态。一旦配置被设置为他们的默认值,就会调用afterCachedConfig方法。该方法,只会在第一次实例化的时候才起作用,Ext.Widget会深度克隆所得到的DOM树(使用cloneNode(true)DOM API)。

第二个实例(及以后)

在创建相同的小部件类的另一个实例的时候,Ext.Widget会使用缓存的DOM树克隆并深度克隆它来创建性能的小部件DOM树。这可避免重新处理元素规范和运行默认值的更新方法的开销。如果配置的更新方法书写正确,该处理过程很大程度上是透明的。

当然,Ext.Widget在复制DOM树后,还有一些工作需要去做,例如检索元素的引用、封装监听并将任何非默认值的配置属性设置到实例。不过,这时的开销就直接与赋予实例的配置值的数量有关而不是类的配置属性了。

重用,循环

下面来研究下如何创建并初始化一个单一的小部件,有几个重要的概念是与在widgetcolumn中使用小部件有关的。

由于限制创建的实例一直是重点,因而缓冲渲染是关键。使用该方法,网格将渲染比记录少得多的小部件,并需要在行移出滚动区域“之后”且新行渲染“之前”循环使用。

当这些转变发生时,widgetcolumn将会将DOM中的小部件移动到新行,通过dataIndex从相应的记录读取字段数据并调用小部件的setConfig来设置它的defaultBind属性,这将会调用apply和update方法,因而,只要编码正确,小部件现在就可以被重新配置来显示新的字段值。

在当前示例的小部件,由于它只表示一个可编辑的值,因而需要在updateValue方法做检查以了解小部件是否已经使用在网格单元格:

column = me.getWidgetColumn && me.getWidgetColumn();
record = column && me.getWidgetRecord && me.getWidgetRecord();
if (record && column.dataIndex) {
record.set(column.dataIndex, value);
}

方法getWidgetColumn和getWidgetRecord通过widgetcolumn被放置到小部件上,因此它知道它在网格中的上下文(context)。

小结

虽然大部分小部件的讨论与网格有关,但小部件也可用于传统部件的任何地方。评分小部件作为迷你小部件引入Ext JS 5已经成为事实。

以下是示例应用程序的主面板截图,显示了它的items数组中的4个实例。

如果对上面的看起来很熟悉,估计是你已经了解了Sencha Touch的模式。虽然这些是Ext JS 5的扩展,Ext.Widget本质上是最初在Sencha Touch中的Ext.AbstractComponent的最后版本。

因此,你会使用小部件来代替组件?在许多方面,编写小部件是比编写组件简单。如果纯粹只使用CSS来处理布局需求,尤其如此。另外,随着我们继续将Sencha移动和桌面框架结合在一起,小部件在未来将会有跨界的可能。

可以在这里找到新的小部件的完整代码示例。享受它并让我们知道你的想法。

作者:Don Griffin
Don Griffin is a member of the Ext JS core team. He was an Ext JS user for 2 years before joining Sencha and has over 20 years of software engineering experience on a broad range of platforms. His experience includes designing web application front-ends and back-ends, native GUI applications, network protocols and device drivers. Don’s passion is to build world class products that people love to use.

【翻译】了解Ext JS 5的小部件的更多相关文章

  1. 【翻译】Ext JS 5的平板支持

    原文:Ext JS 5 Tablet Support Ext JS已被公认为桌面Web应用程序的领先框架.自从平板开始在全球挑战PC的销售,无论是个人还是企业,电脑横向的应用已经产生急剧的变化.Sen ...

  2. 【翻译】Ext JS——高效的编码风格指南

    原文:ExtJS - Efficient coding style guide 作者:Raja 切勿使用"new"关键字:在Ext JS中,使用"new"关键字 ...

  3. 【翻译】Ext JS 6 Beta发布

    原文:Ext JS 6 Beta is Now Available 概述 Ext JS 6的好处 新的Ext JS功能和工具 需要你的反馈意见 概述 很高兴,Ext JS 6 beta版本现在发布了. ...

  4. 【翻译】Ext JS 6早期访问版本发布

    早期访问版本是什么 如何参与 都包括什么 Sencha Ext JS 6 Sencha Pivot Grid Sencha Cmd 6 JetBrains IDE插件 反馈 原文:Announcing ...

  5. 【翻译】Ext JS最新技巧——2014-10-30

    原文:Top Support Tips Greg Barry:Ext JS 5的ExtraParams Ext JS 4同意用户直接将extraParams加入到一个链接,相似例如以下代码: Ext. ...

  6. 【翻译】Ext JS 6.2 早期访问版本发布

    原文:Announcing Ext JS 6.2 Early Access 非常开心,Sencha Ext JS 6.2早期访问版本今天发布了.早期访问版本的主要目的是为了让大家进行测试并评估Ext ...

  7. 【翻译】Ext JS最新技巧——2016-3-4

    原文:Top Support Tips Kevin Cassidy:Grid水印 Ext JS的Grid是一个便于在布局中显示信息的伟大工具.有些用户可能会希望将这些信息打印为会议资料或宣传材料,而且 ...

  8. 【翻译】Ext JS最新技巧——2015-10-21

    原文:Top Support Tips Kevin Cassidy:全宽度的字段错误信息 有考虑过让验证信息显示在表单字段的下面(msgTarget:'under'),但最后发现验证信息被压缩显示了吗 ...

  9. 【翻译】Ext JS最新技巧——2015-8-11

    原文:Top Support Tips Seth Lemmons:使用棒极了的Awesome Font Ext JS 6附带了一个新的海卫一主题,可以使用Font Awesome字体作为背景图像的图标 ...

随机推荐

  1. 04_Struts2标签

    1.通用标签: property标签: 用来输出值栈属性的值 如果value属性没有给出,ValueStack值栈栈顶对象的值被输出 许多情况下,EL表达式可以提供更简洁的语法 url标签: url方 ...

  2. jQuery – AJAX get() 和 post() 方法

    jQuery get() 和 post() 方法用于通过 HTTP GET 或 POST 请求从服务器请求数据. HTTP 请求:GET vs. POST 两种在客户端和服务器端进行请求-响应的常用方 ...

  3. Java第2次实验提纲(Java基本语法与类库)

    1. 使用Git克隆(clone)项目到你的Eclipse项目中 见以下参考资料中的3 从码云将项目clone到你的电脑 重要提示: 使用Git来管理你的代码以后,当你在本机Eclipse项目中开始编 ...

  4. 拟将博客迁移到github

    其实博客园网站速度挺快的, 但是markdown的显示没有github美观. 尤其是代码高亮这一块. 近日发现github pages + vue + github api + stackedit 能 ...

  5. Nodejs 模块查找机制还不错(从当前目录开始逐级向上查找node_modules)

    比如 m.js是能够调用a.js的, 这样子目录就可以避免重复安装node_modules. 够用了.

  6. Nginx之(二)Nginx安装

    首先从官网上http://nginx.org/下载最新的stable version源码,当前最新版本为nginx-1.10.2.tar.gz. 2.1 configure 解压之后,会发现里面有一个 ...

  7. 安卓高级EventBus使用详解

    我本来想写但是在网上看了下感觉写得不如此作者写得好:http://www.jianshu.com/p/da9e193e8b03 前言:EventBus出来已经有一段时间了,github上面也有很多开源 ...

  8. hive高阶1--sql和hive语句执行顺序、explain查看执行计划、group by生成MR

    hive语句执行顺序 msyql语句执行顺序 代码写的顺序: select ... from... where.... group by... having... order by.. 或者 from ...

  9. N个整数(数的大小为0-255)的序列,把它们加密为K个整数(数的大小为0-255).再将K个整数顺序随机打乱,使得可以从这乱序的K个整数中解码出原序列。设计加密解密算法,且要求K<=15*N.

    N个整数(数的大小为0-255)的序列,把它们加密为K个整数(数的大小为0-255).再将K个整数顺序随机打乱,使得可以从这乱序的K个整数中解码出原序列.设计加密解密算法,且要求K<=15*N. ...

  10. Servlet - Listener、Filter、Decorator

    Servlet 标签 : Java与Web Listener-监听器 Listener为在Java Web中进行事件驱动编程提供了一整套事件类和监听器接口.Listener监听的事件源分为Servle ...