使用YUI 3开发Web应用的诀窍Word文档格式.docx
- 文档编号:21293453
- 上传时间:2023-01-29
- 格式:DOCX
- 页数:17
- 大小:33.62KB
使用YUI 3开发Web应用的诀窍Word文档格式.docx
《使用YUI 3开发Web应用的诀窍Word文档格式.docx》由会员分享,可在线阅读,更多相关《使用YUI 3开发Web应用的诀窍Word文档格式.docx(17页珍藏版)》请在冰豆网上搜索。
这样,样式文件就可以放在weather/assets/skins/sam/weather.css,同样,其他图片和样式也可以按这种方式放置,当然我们假设你没有使用YUIBuilder来打包你的项目,这就有点说来话长了。
assets目录和skin目录的含义不言自明,但sam目录就搞不懂啥意思了,其实sam是YUI配置项中skin的默认值,指代YUI内嵌组件的默认样式,sam取名自其设计师SamLind。
因此你也可以使用你的昵称作为你的组件皮肤名称,当然这需要你在YUI全局配置中传入skin参数,简单起见,我们这里只使用默认皮肤。
模块文件模板
这里是一段最常用的模块定义的代码:
/*jslintdevel:
true,undef:
true,newcap:
true,strict:
true,maxerr:
50*/
/*globalYUI*/
/**
*Themodule-namemodulecreatestheblahblah
*@modulemodule-name
*/
YUI.add(‘module-name’,function(Y){
"
usestrict"
;
//handyconstantsandshortcutsusedinthemodule
varLang=Y.Lang,
CBX=‘contentBox’,
BBX=‘boundingBox’,
NAME=‘xxxx’;
/**
*TheXxxxclassdoes….
*@classXxxx
*@extendsWidget
*@usesWidgetParent
*@constructor
*@cfg{object}configurationattributes
Y.Xxxx=Y.Base.create(
NAME,
Y.Widget,
[Y.WidgetParent],
{
//Instancemembershere
},
//Staticmembershere,specially:
ATTRS:
}
);
},’0.99′,{
requires:
['
widget'
'
widget-parent'
],
skinnable:
true
});
前两行是供JSLint过滤这段代码用的,JSLint是我非常推荐的一个javascript语法检查工具。
在JSLint网页版本中,会有一个选项区来设置选项,这排checkbox在页面的底部,如果你使用YUIBuilder,JSLint会自动去读取注释中的配置,来依此对你的代码进行语法检查。
接下来的注释是为了生成YUIDoc,这会为你省去不少麻烦,文档有固定格式的模板,你只要通过注释语法填充内容就可以了。
当项目越来越大,你就会越来越依赖这种项目文档。
现在开始真正的代码段,YUI.add()语句。
我们用YUI.add()来告知Loader模块名称、模块内容、以及其他的一些信息。
模块名通常使用小写字母加中横线组成,这些模块名称就是你在YUIAPI文档左侧看到的名字列表。
当然你也看到有很多不是小写字母加中横线的命名,毕竟这种命名方法并非强制,你可以根据你的习惯做模块命名。
YUI.add()的第二个参数是一个带入YUI实例的函数,这个实例通常被命名为Y,这个回调中的逻辑即是模块的主体部分,而你需要依赖的模块的功能方法都会被挂载到这个Y上。
跳到代码的结尾,你会看到YUI.add()剩下的参数,版本号(也可以不需要)和模块配置项,模块配置项是一个对象,这里代码的含义就是告诉Loader这个模块依赖了widget和widget-parent,并包含皮肤。
当然,在这个例子中widget是多余的,因为widget-parent依赖了widget,这时即便你写上widget,Loader也不会重复加载widget。
而且你根本不必在意模块的顺序,Loader会计算他们的依赖关系并做好排序。
你也会注意到YUIAPIdoc中Loader包含一个addModule()方法,YUI.add的功能就和它很类似。
在函数体内,首先是“usestrict”的声明,这个声明源自ECMAScript5,这使你的代码对于未来的解释器也具备足够好的适应能力,对于旧的解释器也只相当于定义了一个孤零零的字符串值而已,不妨碍程序运行。
“use”声明指定了一个函数作用域,因此将这句声明放在YUI.add()内比放在文件最顶部要更安全一些,因为如果将其放在全局,这回影响后续加载的所有JS逻辑。
然后是定义常量和变量的缩写,JavaScript本身没有常量的概念,这里的“常量”只是一种约定,和在其他编程语言中一样,常量通常使用大写字母和下划线来命名。
这样做的好处有两个,特别是字符串常量,一是当你多次写一个字符串的时候,如果字符串拼错则只能在运行时报错,当改为常量形式如果出错在JSLint语法检查时就能发现。
二是让YUICompressor工作效率更高,使用常量可以节省不少字节数。
通常,我们更多的把配置属性和事件名称定义为常量。
缩写,比如用Lang代替Y.Lang,写的更少,解释器执行更快(直接使用引用而不是逐级查找对象成员)。
在APIdocs所需的文档注释后是实际的代码,我们需要将自己的模块对象挂载在Y上,比如Y.Xxxx。
我建议采用Y.Base.create()来创建模块,这样能创建Base的派生类(Widget即派生自Base),Base模板基本能满足大多数的模块功能需求,因此不必在用其他方式来代替Y.Base.create。
其中的第一个参数是模块名称,NAME属性是Base的一个描述,通常用驼峰命名法,这个名字会用于事件的前缀(例如”io:
success”),抑或是组件容器的class命名前缀(例如:
”yui3-xxxx-content”),此外,NAME还是执行“实例.toString()”时的默认返回值,在debugger模式下经常看到很多类似的log。
这里我们使用常量NAME来标示类模块的名称。
create()的第二个参数是要派生的父类。
比如,你会使用Y.Base作为无UI组件的父类、使用Y.Widget作为带UI组件的父类、Y.Plugin.Base作为插件的父类、或者使用任意派生自Y.Base的子类作为父类,也就是说,任意通过Y.Base.create()创建的类都可以再次通过create()被继承。
第三个参数是一个参元类,摻元类的方法都将挂载到你的类上,常用的参元类包括从Base派生来的ArrayList、从Widget.Attribute派生来的类似Widget-Xxxx的子模块、EventTarget和PluginHost(这两个参元类已经被Base继承),YUI中的继承机制是如此强大,以至于你看Overlay的源码仅仅是一句从Widget的继承。
子类可以继承自多个父类,因此第三个参数是一个数组。
最后是实际的代码实现部分。
第四个和第五个参数都是对象,它们包含了实例,和类中的静态成员。
实例属性通过深拷贝挂载到类的原型(prototy)上,通过this来访问他们,其他静态成员则是可公开访问的。
配置属性
静态成员中最重要的就是ATTRS,其中列出了你的类所需的所有配置参数。
例如你需要一个名为value的成员,这个成员用于保存一个数字,默认初始值为0,可以这样传入第五个参数。
ATTRS:
value:
0,
validator:
Lang.isNumber
}ATTRS中当然可以写入多个成员,每个成员都下辖各自的配置项,通过阅读addAttr()来了解更多参数配置。
我们看到,validator使用了之前代码中定义的缩写Lang,验证函数得到一个实参的输入,返回一个布尔值,所有的Y.Lang.isXxxx都可以作为校验函数,当然,为了更严格细致的校验,你可能需要定义新的validator、setter和getter,这里推荐使用字符串作为句柄赋值给validator、setter或者getter,Attribute会将字符串对应到具体的函数。
比如要定义一个validCodes属性,可以接收单个值或者一个数组,但应当统一返回一个数组:
validCodes:
setter:
‘_setValidCodes’
}我们需要在Y.Base.create()的第四个参数中声明_setValidCodes方法,其他的成员也可以在这里追加声明。
_setValidCodes:
function(value){
if(!
Lang.isArray(value)){
value=[value];
returnvalue;
}除非函数足够简单,最好单独重写setter、setter和validator,传入他们的函数名作为实参,让Attribute将函数名对应到真实的函数。
总之,使用setter作为标准输入,这样会为组件带来一些非常有用的特性。
比如配置项值的改变会触发beforeChange和afterChange事件,如果在beforeChange事件中停止事件,则可以阻止属性的更改,因此组件实例生成后,请善用自定义事件。
比如你如果绑定了afterChange事件,如果触发了回调,那么一定是值被改变了,而不是即便试图更改属性值却在beforeChange中被阻止了。
validator和setter的校验强度是你来决定的,如果你的validator的校验逻辑很严格,就不用再在setter中添加严格的校验,甚至不需要setter,反之亦然,setter校验很严格的话也可以缺省validator。
比如,下面两句代码,你可以任选其一:
validator:
Y.Lang.isBoolean,//tomaketheattributeacceptsstrictlyaBoolean
setter:
Boolean,//tomaketheattributeacceptanyvalueandhaveBooleanturnitintooneSetter也可以作为校验器来使用。
如果输入的值不能转换成attribute可接受的值,setter就会返回Y.Attribute.INVALID_VALUE。
这种情况下,attribute不会发生改变。
当我定义成员的时候,通常会指定这个常量,并放在模块的顶部(和CBX,BBX这些缩写声明一起),例如:
varVALUE=‘value’,
VALID_CODES=‘validCodes’;
这样做的原因是,在一个模块内可能会多次用到这个值,使用常量更加方便。
但是,请千万别在属性声明中使用这些常量。
//***Don’tdothis***//
VALUE:
}这样书写会生成一个名为VALUE而不是value的属性成员,同时记住,不要覆盖基类中的属性成员,Widget中已经有许多属性成员(见表格),如boundingBox、visible、disabled、height、width等,你也可以修改他们,Y.Base.create()会将父类和基类(Base)中的属性成员合并,所以要重载已有的属性成员,你只要在派生类中重复定义即可。
当你要定义的属性成员是数组或者对象的时候要尤为小心,对象(数组)是一个引用值,如果初始化的属性中含有object,他们(所有实例的这个成员)指向的会是同一个object。
当你更改这个object或者删除的时候,所有的实例也都跟着改变(prototye永远指向父类的prototye),所以,Base提供了克隆的机制,在你传入object属性成员的时候克隆一份,这会避免引用重复的问题,其他objects的初始化,请使用valueFn参数,或者直接在类的构造函数(initialize)中构建他们。
其他静态成员
另外,你需要给Y.Base.create()的第五个参数上挂载两个静态成员,如果你开发一个插件(plugin),你需要传入NS(命名空间Namespace的缩写)参数,否则插件就无法工作,NS是一个字符串,作为在宿主对象中的一个属性,插件将挂载其上,不过你要特别小心,选择NS命名的时候不要覆盖已有的插件命名。
如果你开发一个需要渐进增强的组件(widget),你需要传入HTML_PARSER参数,HTML_PARSER是一个object,其成员可以是从配置参数传入的已有的HTML片段获取,也可以来自于一个(CSS3)选择器或者一个getter函数。
请参考Widget使用指南中的ProgressiveEnhancement来了解更多。
当然,你有时会提供一些类静态成员(属性/方法),并暴露给开发者去调用,而类中的常量由于闭包的保护对外并不可见,如果你想对外暴露他们,则需要通过Y.Base.create()的第五个参数传入一个boject,object的成员都会被当作静态成员挂载在类本身上。
例如WidgetStdMod中的HEADER,BODY和FOOTER常量就是用这种方法声明的静态常量,不过你需要通过Y.WidgetStdMod.BODY来访问。
实例成员
Y.Base.create()的第四个参数(object)会mix到类原型上,通常我们先声明属性后声明方法,没有原因一定要这样,只是惯例,声明的顺序也不影响程序的执行。
这样约定是为了更工整的组织你的代码,尽管你在类构造器中也可以创建成员,但还是强烈建议先声明后赋值,声明的位置用来为APIdoc补充注释,这样的coding方式大大增强源码的可读性。
私有属性通常带有下划线前缀,类的公共接口最好通过Attribute传入,而不是直接挂载实例属性,直接挂载属性很不安全,毕竟它不像通过Attribute可以带入校验器、适配参数类型(通过setter函数),并且可以支持AOP的一些特性诸如触发valuechange事件等,你会发现通过Attribute传参的好处不胜枚举。
在使用Attribute的时候,注意不要将成员初值设为对象或数组,因为它们都会指向同一个object而使程序出错,最好将那些object成员初值设为null,也不要将属性留空,如果实在不知道成员的数据类型,统统设置为null,因为在调试程序的时候,引用那些没有初值的成员会报一个“类型错误”。
Base中的实例方法
你可能会注意到,在子类中并没有定义类构造器,因为在构建实例的时候,Base会通过调用initializer方法(如果存在的话)对实例进行初始化,Initializer使用的参数即是实例的构造参数,所以可以把initializer看做构造器,所有派生自Base的类在创建之时先要被Base“扩充”(Argument)一下,Base这里被称之为“摻元类”,参元类在调用initializer之前通过Attribute来传入参数,对于设置了HTML_PARSER的Widget来说,Attribute中的属性是从HTML“标签”中获取,而不是直接从参数中读的。
Initialize方法干了这么几件事情,首先是将所有需要初始化的属性设置为object或array,然后注册(publish)自定义事件,虽然EventTarget可以直接在没有注册事件的情况下fire事件,但我还是建议先注册事件类型(为了事件可以冒泡),同样,类似先声明后赋值的原则,你可以在注册事件的地方补充APIDoc注释,乍一看似乎在函数体内写APIDoc注释很奇怪,但你也找不到更好的写APIDoc注释的方式。
接下来就是处理Initializer函数接受的参数,你也可以在参数中增加配置属性中没有的内容,比如我们将on,after,buggleTargets和plugins等Base原本没有的属性(或方法)挂到Base上(参见Base),也可以给Widget父类挂载新的子属性,尽管你在实例化一个对象的时候只传入一个参数,但这个参数所包含的内容不止与此。
Javascript没有析构函数的概念。
Base允许你声明一个名为destructor的函数来释放内存资源,依此来模拟析构函数。
但这只是一个折衷方案,在删除一个对象时,javascript编译器不会自动回收其内存,因此对于没用的对象是需要手动销毁的,即调用一下析构函数。
开发者在使用你的类的时候,不需要直接调用initializer和destructor,Base会在必要的时候调自动用他们。
Initializer会在对象实例化时调用,destuctor会在用户调用destroy方法销毁对象时调用。
绑定的事件没有被解绑常常会造成内存泄漏。
Widget会自动解除组件内部绑定的事件,其他的手动绑定的事件不会被自动解除。
Base也不能解除所有事件监听,我通常使用这段代码来解除事件绑定,先在声明私有成员_eventHandlers:
_eventHandles:
null,然后在initializer方法内将_eventhandlers设为数组
initializer:
function(cfg){
this._eventHandles=[];
//……
},同样在initializer函数(或者在Widget的bindUI函数)中,通过这段代码来绑定监听:
this._eventHandles.push(this.after(‘someAttributeChange’,this._afterSomeAttributeChange,this));
在destructor方法内:
destructor:
function(){
Y.each(this._eventHandles,function(handle){
handle.detach();
});
},这里在initializer中,有可能绑定了自定义事件(除了UI元素上的事件),就像上文提到的,Attribute是处理参数的标准方式。
任何非数据的存储都由事件处理逻辑完成。
在上面的例子中,我使用_afterSomeAttributeChange来监听someAttribut的属性变化。
事件发生会触发回调,并带入即事件对象(我通常以ev表示),事件对象包含几个与事件相关的属性,其中一个属性newVal表示变化后的值。
Widget的实例属性
Widget有两个重要属性BOUNDING_TEMPLATE和CONTENT_TEMPLATE。
初始值都是”<
div>
<
/div>
”,这为多数widget提供一个标准html结构,包含双层嵌套的div。
但并不是所有的widget都适用,比如按钮使用<
span>
<
a>
/a>
/span>
而不是<
结构,其实Widget不强制要求使用一个大容器(contentBox),这两个属性可以使用任意html标签,比如我用a来描述按钮
BOUNDING_TEMPLATE:
‘<
’,
CONTENT_TEMPLATE:
null,<
在你不需要contentBox的时候就将其设置为null,这时contentBox的配置会继承自boundingBox。
不要将widget的所有HTML都放在这两个属性中,这两个属性中的元素要尽量简单,在renderUI中使用代码创建其余的html标签。
Widget会给所有你指定的标签加上id和class,形式诸如yui3-xxxx,yui3-xxxx-visible或yui3-xxxx-disabled,其中xxxx是小写的NAME属性。
Widget实例方法
Widget的初始化有多个步骤,除了会在对象实例化时调用initializer(构造函数)外,在销毁时也会调用destructor函数(析构函数),这两个方法都继承自Base。
Widget又增加了renderUI,bindUI,syncUI三个函数,在widget的render方法被调用时,这三个函数会依次执行。
renderUI负责生成widget最基本的HTML骨架,包括boundingBox和contentBox,如果考虑到渐进增强,renderUI会先检查元素是否已存在于DOM中,这是因为如果设置HTML_PARSER属性,配置属性中定义的元素会被事先生成好。
如果元素没有事先生成,我们才需要通过renderUI来创建。
最简单的生成HTML的方法(假设没有使用渐进增强)就是使用Y.Node.create方法:
renderUI:
varcbx=this.get(CBX);
cbx.append(Y.Node.create(Y.substitute(Y.Xxxx.TEMPLATE,CLASS_NAMES)));
},这段代码的运行需要特定的上下文,首先,要声明常量CBX(见本篇文章第一个代码块),其次,widget所需的模块都加载完全,加载widget之前会自动加载其依赖的Node,Y.substitute是可选模块,如果需要则要把’substitute’加入到requires中。
此外还要有一
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 使用YUI 3开发Web应用的诀窍 使用 YUI 开发 Web 应用 诀窍