`
houfeng0923
  • 浏览: 142687 次
  • 性别: Icon_minigender_1
  • 来自: 大连
社区版块
存档分类
最新评论

YUI3学习(九)---组件框架之 Widget

 
阅读更多

 

Widget部件类是YUI3所有部件的基础类。它在Base的基础上提供了以下几个核心的基础功能:

1,在Base的init和destroy时刻,添加render生命周期管理。

2,抽象的渲染相关方法,促进一致的MVC模式访问。

3,统一的部件属性设置方法。

4,一致的标记生成支持。

5,一致的css样式名称生成支持。

6,内建的渐进增强支持。

提示:3.1.0版本后的widget组件,其默认的css前缀命名由‘yui-’变为‘yui3-’。以此避免在同一程序中YUI2和YUI3的css类名冲突。

 

1.Widget提供哪些功能

1.1Widget类的结构和职责

 

Widget类作为创建YUI3部件的基础类。通常通过继承它来创建其他的Widget。Widget基类继承自Base。因此它提供了同样的属性管理、事件及插件支持等功能。

此外,Widget还增加了以下核心功能:

基础属性

Widget定义了 一组基础属性,每个Widget实例都可以使用。如boundingBox、contentBox、width、height、visible、focused、disabled等。

渲染阶段

Widget在Base类的init和destroy过程基础上增加了render方法和事件。

抽象渲染方法

Widget定义了一些抽象的渲染相关方法:renderUI,bindUI,syncUI。为Widget子类的渲染提供了统一的入口。

渐进增强

Widget在初始化过程中,提供了一致的渐进增强入口。它还提供了一种方法,以隐藏渐进增强标签,避免无样式内容的闪动。

本地化字符

Widget的strings属性支持字符串本地化。当与internationalization功能使用时,可以将需要本地化的字符串与核心代码分离。

 

1.2Widget的基本属性

Widget为YUI3所有部件类提供了一组共同的属性。下表所示

 

boundingBox Widget的最外边框node,可以设定部件的尺寸和位置,也可作为装饰皮肤的容器。
contentBox 内容容器;也是建立look and feel 的node。
srcNode Dom中已经存在的node,默认会解析为contentBox
tabIndex 见名知意,应用于boundingBox
focused 标记boundingBox是否获取焦点,boundingBox 会被赋予 [focused]的css样式
disabled 标记boundingBox是否失效,boundingBox会被赋予[disabled]的css样式
visible 标记boundingBox是否可见,boundingBox会被赋予[hidden]的css样式
height boundingBox高,带单位字符串或数值,数值的单位取决于widget的DEF_UNIT定义
width boundingBox宽,带单位字符串或数值,数值的单位取决于widget的DEF_UNIT定义
strings 可用作部件外观的字符集合,可支持字符本地化

 

1.3渲染相关方法

 

Widget在init和destroy阶段增加render方法提供渲染阶段的支持。render方法作为渲染部件的入口,通过添加/修改DOM节点来构建自己的UI并且设置监听来激活UI。

有了这个渲染阶段,widget就可以将状态和逻辑与UI展现相分离。这种分离使widget的状态可以在它显示或渲染到页面前安全的修改和查询。

此外,分离关注点的方式也将代码分为操作widget状态变化及处理widget相关业务逻辑的方法与修改DOM结构的方法。这样的设计也使自定义功能和关注点测试变得容易。

 

生命周期方法init、destroy、render

与init和destroy一样,render方法在Widget中是不能重载的(final)。具体的渲染逻辑实现委托给了Widget实现的renderer方法。下面分别介绍这三个方法。

init(继承自Base的方法)

init方法从上到下(Base fist,subClass last)遍历widget的类层次,将每个类的ATTRS静态属性配置给实例,然后调用类的initializer方法。

init方法触发init事件,可以在事件监听器中阻止初始化过程。

destroy(继承自Base的方法)

从下到上(subClass first,Base last)遍历widget的类层次,调用每个类的destructor方法。

destroy方法触发destroy事件,可以在事件监听器中阻止销毁过程。

render

与遍历widget类层次结构调用initializer和destructor不同,render只调用Widget实例对象的renderer方法。

render方法接收‘parentNode’参数,该参数是Dom文档中已经存在的节点。当widget渲染后,会被添加进该节点内。

如果widget的contentBox或boundingBox不是已经存在于document中的节点,并且‘parentNode’参数未设置,那么widget将作为document中body节点的第一个子元素插入到文档中。

render方法会触发render事件,可以在事件监听器中阻止渲染过程。在widget实例创建后,调用render()方法开始渲染。

 

Widget的renderer方法

Widget提供了一个renderer方法的实现,在多数情况下不需要重写该实现。源码如下:

 

renderer:function(){
    this.renderUI();
    this.bindUI();
    this.syncUI();
}

其中renderUI,bindUI,syncUI三个方法在Widget中作为空方法为开发者提供了统一的模式。每个方法都承担了不同的角色职责。

 

renderUI

该方法的职责是创建并添加widget需要的dom节点(或者为支持渐进增强,修改存在的节点)。一般是widget开始修改DOM的入口方法。

bindUI

该方法的职责是为绑定事件监听。将UI变化与widget的状态关联起来。这些监听通常是属性change监听,当属性变化时更改UI显示状态;

同时也用于绑定UI的DOM事件监听,将widgetUI和widget的API方法关联起来。

syncUI

该方法的职责是在widget渲染的时候根据当前widget状态设置初始UI状态。

 

1.4渐进增强

 

概念理解渐进增强,对应 ‘平稳退化’Graceful Degradation” 

 

Widget为需要提供渐进增强支持的部件实现了标准的入口-----静态属性HTML_PARSER

HTML_PARSER定义了一个存储selector(选择器)和function(参数为srcNode)的hash对象,用于从已经存在的DOM节点中为widget解析内容并在初始化时为widget提取属性配置值。

在Base.build()方法介绍时提过,Widget通过_buildCfg方法设置HTML_PARSER属性,在build时会将HTML_PARSER及ATTRS作为属性添加到实例中。源码片段如下

Widget._buildCfg = {

    aggregates : ["HTML_PARSER"]

};

以下是简单实例

 

MyWidget.HTML_PARSER = {
     // 设置与Node相关的选择器属性。根据值类型分别调用node.one(),node.all()
     titleNode: "span.yui3-title",
     itemNodes: ["li.yui3-listitem"],

     //设置函数类型属性。函数执行上下文为widget实例。
     label: function(srcNode) {
        return srcNode.one("input.yui3-title").get("innerHTML");
     },
     xValue: function(srcNode) {
         return srcNode.one("input.yui3-slider-xvalue").get("value");
     },
     yValue: function(srcNode) {
         return srcNode.one("input.yui3-slider-yvalue").get("value");
     }
};

 

HTML_PARSER属性在widget调用initializer方法时被读取。如果key对应的值是selector,将其转换为对应的Node或NodeList;如果对应的function,将其转换为返回值。

并将HTML_PARSER解析的属性值与构造器传入的config参数属性值进行合并。

 

隐藏渐进增强标签

Widget除了提供了通用的入口支持渐进增强,也提供了在初始化时隐藏渐进增强内容的CSS接口。

1,如果JavaScript不可用,widget的内容应该是可见可访问的。

2,如果JavaScript可用,widget相关的html标签应该尽早的隐藏,避免在JavaScript和css添加到页面前,渲染widget出现的无样式内容闪动。

3,widget对象只有在渲染完毕后才显示。

为了实现这一效果:

1,YUI种子文件激活后会为页面<HTML>标签增加‘yui3-js-enabled'css类。

2,开发者可用在widget上增加名为’yui3-widget-loading'的css 类名,代表加载中的状态,可用和‘yui3-js-enabled'一起使用,在widget加载时隐藏widget内容。

代码如下所示 ,默认提供了对所有widget及特定widget的支持。

 

.yui3-js-enabled .yui3-widget-loading {
    display: none;
}

.yui3-js-enabled .yui3-overlay-loading {
    /* Hide overlay markup offscreen */
    position:absolute;
    top:-1000em;
    left:-1000em;
}

 

在widget渲染完成后,renderer方法会自动从boundingBox和contentBox节点上移除’loading‘样式,将功能完备的widget显示出来。

使用时,只需要按如此约定定义样式并在widget box节点上设置该样式即可。

 

1.5标签结构

Widget类为渲染提供了通用的标记格式-通过boundingBox和contentBox属性。

绝大多数的widget通过bounding box来包裹一个 content box。默认它们都是DIV元素,也可以通过覆盖原型属性BOUNDING_TEMPLATE和CONTENT_TEMPLATE属性来

自定义设置其他元素。然而,开发者也可以设置CONTENT_TEMPLATE属性为null,这样widget实现就不支持双层结构,其boundingBox和contentBox将指向同一个节点对象。

 

如果在构造调用时没有指定srcNode(srcNode->contentBox),widget会自动创建并在render方法调用时添加进document;如果存在,就作为widget的contentBox;

如果在构造中传入id,widget会用这个id对应的node作为boundingBox;

如果在render方法调用时传入node或selector,widget会用这个参数对应的node作为boundingBox的父节点,将widget添加进去。

1, boundingBox

  widget的最外层容器,其功能性大于视觉性。

  默认class名称:yui3-widgetname, 如果部件继承了多个widget基类,那么class 包含所有的widget名称。

  Widget实例的状态也会直接体现在class中,当状态隐藏或无效时,class名称中会添加 yui3-[widgetname]-hidden/disabled;

  Widget的位置及宽高属性会直接应用boundingBox的宽高及位置属性上。

  不要试图在boundingBox上应用视觉样式,它仅仅定义css的布局和定位样式。

2, contentBox

是boundingBox的child节点;widget会在contentBox中添加节点来创建UI和内容。

  也定义了默认的class名称:yui3-[widgetname]-content .

  视觉样式可以应用在contentBox上。如 border、padding、background-color 等。

  在构造中指定srcNode属性定义contentBox。

为什么采用双层容器盒子模型?

1, boundingBox担当所有装饰元素的容器,包含所有装饰contentBox的其他元素。比如:用以实现圆角、阴影、衬垫等装饰元素,作为content box的兄弟元素,当他们和content box有同一个父元素时,对他们的定位、大小的设定会更方便。

2, 为widget和插件提供了一致的访问结构,便于编写可复用的装饰器或插件。

3, 利用一个没有border和padding的box,便于提供一致的widget宽高量算模型。(跨浏览器,在不同盒子模型下有一致的宽高)

 

1.6类名和css

为了给所有的widget提供一致的class样式名称,Widget类提供基于ClassNameManager类的两个方法,依靠widget的NAME属性来定义class名称。

Y.ClassNameManager.getClassName(args*)、 Widget.getClassName(args*)及this.getClassName(args*)

生成上节中提到的class 样式名:yui3-widgetname-arg1-arg2…(widgetname为widget的NAME属性的小写形式)

Widget的状态基本都有对应的css渲染其状态样式,可以通过这个方法返回某个状态下的class名称。Widget避免使用属性控制像visible、disabled和focused的样式,而是通过如下的的class名标记boundingBox来实现:

visible yui3-[widgetname]-hidden

disabled yui3-[widgetname]-disabled

focused yui3-[widgetname]-focused

 

 

// with Spinner.NAME = "spinner";
renderSpinnerButton: function() {
    // 生成class名: "yui3-spinner-increment-major"
    var btnClassName = this.getClassName("increment", "major");
    //与以下方法等价
    Y.ClassNameManager.getClassName(Spinner.NAME, "button");
}

 然而,如果想提供IE6兼容格式,就需要为每个widget的每个状态定义相应的css名。

 

 在以上基于widget状态的CSS规则中,每个widget都需要定义“yui3-[widgetname]-hidden”规则来实现对可见性的控制。

 是否要提供另两种对状态的支持,取决于这个widget是否需要为“disabled”和“focus”两种状态提供特殊的UI控制。

 

1.7默认的UI事件

widget可以为任意DOM事件发布和触发自定义事件。这些事件会在boundingBox内触发。与其他的widget自定义事件一样,需要以widget的name作为前缀:menuItem:click

事件监听器的默认上下文对象是触发事件的widget本身,而不是触发DOM事件的节点。这样使监听UI事件不需要考虑widget的DOM元素。

考虑到大多的widget实例都会发布和触发一些事件,Widget类默认提供了以下支持以保证这些事件的触发机制在不同的widget实现中都是一致的:

1,Widget开发者不需要明确的发布一个已知的UI事件让widget的使用者来监听。

出于性能考虑,这些事件仅当被监听的时候才创建,并且触发这些事件的是同一个基于委托的DOM监听器。

2,为了更精确的控制事件的某些特性,Widget开发者依然可以选择发布已知的UI事件。

最好的例子是为已知的事件提供一个默认处理器。比如为MenuWidget发布‘click’事件,目的是提供 默认的点击行为支持。

3,Widget发布的已知DOM事件定义在其原型属性UI_EVENTS中。该属性默认值是Y.Node.DOM_EVENTS。开发者可以通过这个属性增减自动发布/触发的事件。

相关源码详细参考 

 

 

2,开发自定义的Widget

 

开发自定义的Widget,需要创建继承Widget的子类并实现如图所示的属性和方法。

首先定义构成widget的属性(ATTRS静态属性),这些属性构成了公布给程序的状态和API方法。

然后根据上面介绍的职责分类分别实现initializerdestructor、renderUI、bindUIsyncUI方法以及其他的API相关方法。

Extending The Widget Class这个例子教你如何一步一步实现一个数值选择器widget。    

此外,YUI提供了自定义Widget开发模版文件,可以作为开发自定义Widget的基础。

 

2.1插件和扩展

除了创建Widget子类来实现自定义Widget,YUI3还提供了两种重用代码的机制:基于Base的Plugins和Extensions。

将Widget的功能打包成扩展或插件,就可以在多个类中扩展(extension)或在多个实例中插入(plugin)。

 

插件还是扩展?

在Widget开发中总会遇到相关功能和特性是以插件形式存在还是以扩展形式存在?开发者需要根据Widget的用意来考虑如何设计。

插件和扩展提供了一个创建小模块功能的机制,这些小功能可以添加到widget的核心实现中。它们的差异如下:

Extensions---基于类级别

1,扩展提供的功能在类级别中使用。

2,扩展是基于扩展功能创建新的widget类。

3,如果新的功能是类所必须的,就应该存在于扩展中。

4,WidgetParent,WidgetChild,WidgetPosition和WidgetStack是YUI3提供了扩展开发的良好示例。

  例如,一个Tree部件需要实现父/子结构支持,而Menu部件也同样需要,我们想重用父/子结构支持而不能通过强制它们都实现共同的基类,另外,父子结构功能支持对它们来说是必须的。

5,扩展通过使用Base.build方法(或Base.create,Base.mix)合并类。

 

Plugins---基于对象实例级别

1,插件在实例层次为对象提供额外的特性。

2,开发者使用插件为对象应用额外特性。

3,如果新的功能不是必须的,那应该使用插件。

4,YUI3提供的Animation和IO插件是插件开发和使用的良好示例。

    不需要把Animation和IO的功能写到具体的类中。

5,使用实例对象的plug方法来应用插件。

 

2.2Widget扩展

当你准备基于YUI3开发Widget时,可以使用Base.build来扩展框架提供的以下几个打包好的扩展模块。

 

widget-position 添加XY 定位功能
widget-position-align 添加具有对齐和xy定位功能
widget-position-constrain 添加具有位置限制的功能
widget-stack 添加堆叠(zIndex)功能
widget-stdmod 添加标准模型(head body footer)支持
widget-parent 添加构件包含子构件并提供管理和选取的功能
widget-child 添加允许作为构件子构件的功能支持

 

其中widget-parent和widget-child扩展,允许开发者创建嵌套的Widget类,值得详细介绍一下:

widget-parent

支持widget部件作为容器包含、管理和选取子widget。

1,提供了一个统一的方法来创建父子关系

 

parent.add(child,n);
parent.remove(n);

 

2,关系可以是单一的层次关系(Tabs与TabLists)或嵌套的多层次关系(Menus与MenuItems)。

3,父类自动设定为子类事件的目标(event target),可以利用事件冒泡在更高层次上监听事件。

4,当父类渲染时会自动渲染子类。

5,父类合并了YUI中ArrayList的api方法,提供了迭代和遍历子类的功能。

 

parent.each(function(child){});
parent.item(n);
parent.indexOf(child);
parent.size();

 

6,提供选择支持,包括non-binary选择(all, none, some)支持。

7,最后,父类提供了通过在构造器中传入字面量对象参数来添加子类的方法。

 

var tabview = new Y.TabView({
    children: [{
        label: 'foo',
        content: '<p>foo content</p>'
    }, {
        label: 'bar',
        content: '<p>bar content</p>'
    }]
});

 

 

widget-child

支持将widget添加进widget-parent中。与widget-parent共同使用,允许你建立类父子结构。

与widget-parent一样,widget-chlid也提供了一致的接口方法,支持子类与子类、子类与父类的交互。

如:child.next();child.previous();child.ancestor();

YUI3官方示例提供了如下扩展例子可作为开发参考

使用 Extensions: Building Custom Widget Classes  

扩展Widget-Position, Widget-Stack: A Simple Tooltip Widget  

扩展Widget-Parent, Widget-Child: A Heirarchical ListBox Widget  

 

此外,还可以参考YUI3 框架中基于扩展实现的Widget

Overlay

Uses widget-position, widget-position-align, widget-position-constrain, widget-stack, widget-stdmod

TabView

Uses widget-parent, widget-child

YUI官方给出了实现扩展的模板文件:http://developer.yahoo.com/yui/3/base/assets/myextension.js.txt

 

 

2.3Widget插件

YUI3在3.1.0版后提供了几个widget插件。并且提供了几个代码示例描述如何创建自定义插件。

Widget Animation Plugin (api documentation) 

Widget (and Node) Drag/Drop Plugin (api documentation)   

Creating Widget Plugins (example)   

Creating An Overlay IO Plugin (example)    

Creating An Overlay Animation Plugin (example) 

 

此外,YUI Gallery中也有不少的widget插件,如 Modal Overlay PluginWidget IO Plugin

 

YUI官方给出了定义插件的模板文件:http://developer.yahoo.com/yui/3/plugin/assets/myplugin.js.txt

 

2
2
分享到:
评论
2 楼 huxingjie 2012-12-05  
硬是没看懂。。悲催。。
1 楼 shabumaiev 2012-03-01  
LZ 你真牛X!给自己加油!!

相关推荐

Global site tag (gtag.js) - Google Analytics