NUI是一个基于W3C标准的Web前端框架,是一个跨浏览器与后台技术无关的框架,由网易邮箱的前端开发团队研发,使用NUI框架可以快速、高效进行前端的项目开发和维护,NUI框架轻量,但是功能丰富,包括常用的工具类如Ajax、Element、Event,简洁的OOP模式,强大的UI组件,基于OPOA的framease页面模块管理体系。
NUI框架主要包括Base库、组件、framease
NUI的oop库主要包括以下几个功能实现:
1.类的创建
2.对象和类的继承
3.对象和类的属性方法扩展
详细可参考:
netease.mail.base.Object
基本类型扩展模块
对js的基本类型进行扩展,包括字符串、数组等类型扩展一些实用的方法,详细可参考:
netease.mail.base.String
netease.mail.base.Array
netease.mail.base.Function
netease.mail.base.Date
netease.mail.base.Number
工具类
工具模块包括dom,ajax,event等常用的工具包
详细可参考:
netease.mail.base.Element
netease.mail.base.Event
netease.mail.base.Ajax
netease.mail.base.Cookie
Loader
实现框架的模块加载等管理功能。
netease.mail.base.Loader
NUI的组件库是网易邮箱前端开发小组自主开发出来的,并在这个组件库基础上可以进行二次组件开发。
基础组件类是
netease.mail.jsstyle.component.Component
NUI组件库一些地方也借鉴了extjs的思想,主要体现在以下:
可以实现组件类层级继承结构
基于component - element - html dom的模型关系
自定义事件的机制
比起extjs NUI组件库也有自己的一些特点,包括:
更加轻量级,包括极速4.0的公共组件库只有100多k
效率更高,经过不断的优化,在执行效率、渲染速度、内存回收的效率也更加高
除了可以生成component对象的方式,还可以实现获取组件对象的html,这样甚至可以在服务器端使用组件库通过node.js等来生成页面
可以使用html模板方式来创建新的组件类
极速4.0公共组件库是在NUI基础组件库基础继承开发,并在极速4.0上使用的组件库,包括的类有:
netease.mail.jsstyle.component.Button
netease.mail.jsstyle.component.Checkbox
netease.mail.jsstyle.component.Input
netease.mail.jsstyle.component.Link
netease.mail.jsstyle.component.Menu
netease.mail.jsstyle.component.Msgbox
netease.mail.jsstyle.component.Radio
netease.mail.jsstyle.component.Tab
netease.mail.jsstyle.component.Tips
netease.mail.jsstyle.component.Title
netease.mail.jsstyle.component.Toolbar
Framease 是一个基于opoa(one page one application)的解决方案,实现在一个页面上无刷新的页面跳转的前端浏览模式,使用Framease可以简单快速的实现基于页面模块的ajax应用
开发者引用NUI包:
压缩版本(gbk):http://mimg.127.net/nui/nui.0.0.2.min.js
压缩版本(utf-8):http://mimg.127.net/nui/nui.0.0.2.min.utf8.js
var oEl = new netease.mail.base.Element();
NUI的所有base和component下的类都可以使用段引用来使用
var oEl = new NUI.Element();
var oComponent = new NUI.Component();
在NUI中,为了可以简单高效利用js的oop,我们会将区分js中的几种对象:
普通的object对象,包括function以外的所有对象
function对象,即为类对象
类的prototype对象,即为类的原型对象
NUI组件是以Component类为基础的组件体系,在Component类基础上,使用面向对象的方法,并提供极速4.0的一系列公共组件,开发者也可以在这基础上进行二次开发自定义组件
创建Component对象最简单的方式是new出一个实例对象,比如:var component = new NUI.Component({id : "baseComponent"});
上面例子使用配置对象生成组件实例,Component以及继承组件都支持可选基本配置对象,包括的属性有:
id : 组件唯一id
默认为组件自己生成的id,配置此属性后为自定义id,可以通过NUI.find("#id")获取组件对象
renderTo : 渲染的容器
以appendChild方式渲染,容器可以是dom,dom id,element对象或者组件对象
如果不希望以appendChild方式渲染的话,创建后使用render方法渲染,比如:
var oLink = new NUI.Link();
oLink.render("divid","before");
详细可以参考render接口说明
listeners : 监听事件的数组,
详细可以查看下面关于组件事件的说明
disabled : 是否禁用,
禁用时所有事件不起作用,禁用的界面效果需要组件支持,暂时极速4.0的公共组件只有Input和Button支持
hidden : 是否隐藏
是否隐藏的方式渲染
autoRender : 是否自动渲染
自动渲染时,在组件创建完之后自动渲染到界面上
xtype : 以xtype的组件类型创建组件
items: 子组件数组,数组由组件的配置对象组成
在NUI组件事件模型中,因为引入的自定义事件,所以事件分为自定义事件和原生事件,组件事件对象结构:
name : 事件名称(可以是dom原生事件或者是自定义事件),
handler: 事件绑定的函数,
target: 当事件为dom原生事件时,指定绑定事件的dom对象
type : 自定义还是原生事件监听(custom|dom)
会在两个地方出现使用组件事件对象
1.组件创建时候配置的listeners属性数组,默认为type:custom,当配置type:dom时为原生dom事件
2.组件的全局事件定义数组静态属性_listeners数组,默认为type:dom,当配置type:custom时为自定义事件
无论是自定义事件还是原生dom事件,handler的作用域都是组件的实例对象
在基础类Component定义了一些通用的自定义事件:
render: 渲染后立即调用
fullrender: 发生在render事件后,用在其他有render事件的依赖
destroy: 组件被销毁后调用(即调用组件对象的destroy方法后触发事件)
其他的子组件的自定义事件可以参考每个组件的文档说明
自定义事件的监听除了上面已经提到有两个地方的配置方式定义,也可以在创建组件对象后使用listen方法监听,并提供unlisten和unlistenAll方法解除监听,使用fire方法触发事件,详细参考各个方法的文档说明,除了这些监听的方式,一些使用得比较多的事件可以通过配置对象直接定义,比如:
var oLink = new NUI.Link({click : function(){
alert("clicked.");
}});
具体可参考各个组件提供的配置对象说明
使用组件方式创建html dom,如果删除时候没有取消相关事件监听或者对象的引用,会导致内存泄露无法回收,正确规范的使用可以避免内存泄露
当需要删除页面上的组件对应的dom元素时,应避免使用innerHTML为空或者removeChild等原生调用方式,可以使用以下方式:
1.组件对象的destroy调用
2.非组件对象时调用dom的element的remove方法
3.当需要清空一个容器包含大量组件时,也可以直接调用容器的element对象的remove方法来清空,这时remove接口会判断里面的组件会逐一destroy
删除组件对象时使用对象方法destroy,这里会将组件的相关对象,element对象,dom对象等之前的引用删除,避免出现内存泄露,如果由开发者自己保存组件对象的引用,需要在destroy事件里面删除引用,比如以下例子:
var myObj = {};
var oLink = new NUI.Link({renderTo : document.body});
oLink.listen("destroy",function(){
delete myObj.link;
});
myObj.link = oLink;
...
oLink.destroy();
一般情况下可以使用new方式获取一个组件实例对象,然后渲染在页面上,但是有些情况下需要生成大量的组件,并且出现组件和非组件html结构嵌套,这时可以使用组件提供的html方法可以直接组件对象提供html方法或者组件的html静态方法,比如:
var aHtml = [];
for(var i=0;i<100;i++){
var oLink = new NUI.Link({
listeners : [
{
name : "click",
handler : function(){
}
}
]
});
aHtml.push(oLink.html());
}
NUI.$("dvContainer").html(aHtml.join(""));
上面这个例子创建了100个link组件,并且以html字符串方式加起来然后再以innerHTML的方式原生渲染到一个div容器里面,不过这里出现一个问题,所有页面上生成的链接点击都没有反应,这时因为这里只是单纯生成html,并没有监听任何链接的dom事件,所以这里需要调用一个方法绑定事件:
NUI.Component.setEvents();
组件对象提供find方法查询子组件,each方法子组件迭代,getChilds获取子组件,getRoot获取顶级组件
会在两种环境下使用组件嵌套,组件的二次开发下使用和组件调用下使用
组件调用者可以使用add方法添加子组件,子组件的dom默认会添加到父组件外层容器里面(容器可以_getChildContainer方法指定),add的参数可以支持组件对象或者是组件的配置对象(需要配置xtype),除了使用add方法,也可以在创建组件时配置对象使用items来配置
以下是使用极速4.0的Link使用Icon子组件来演示以上例子:
var oLink = new NUI.Link();
oLink.add({xtype : "icon",type : "info"});
var oLink = new NUI.Link({
items : [{xtype : "icon",type : "info"}]
});
极速4.0的大部分公共组件为了更好重用组件,都会使用组件嵌套方式,比如Button组件嵌套了Link组件,在二次组件开发中,我们会使用以下方法来生成子组件:
_getChildHtml:获取子组件的html
_getChildDom:获取子组件的dom
_batch:批量调用以上两个方法
组件的更新可以使用update方法,传入配置对象中需要更新的属性来更新组件,不过只能传入除items以外的非基本属性,比如id,renderTo属性将不起作用
要学习及应用好NUI框架,需要理解Html DOM、NUI Element及Component三者的区别。
NUI组件最大的特点,是开发了一系列非常简单易用的组件,我们只需要使用这些组件就能实现各种丰富多彩的UI的开发。
无论组件有多少配置属性、还是事件、方法等等,其最终都会转化为HTML在浏览器上显示出来,而每一个HTML页面都有一个层次分明的DOM树模型,浏览器中的所有内容都有相应的DOM对象,动态改变页面的内容,正是通过使用脚本语言来操作DOM对象实现。
仅仅有DOM是不够的,比如要把页面中的某一个节点移到其它位置,要隐藏或显示某一个节点等,我们都需要通过几句 javascript才能完成。
因此,NUI在DOM的基础上,创建了NUI.Element,可以使用Element来包装任何DOM,Element对象中添加了一系列快捷、简便的实用方法。
对于终端用户来说,仅仅有Element是不够的,比如用户要显示一个自定义按钮、要显示一个菜单、要显示一个弹出窗口等。因此,除了Element以外,NUI 还建立了一系列的客户端界面组件Component,我们在编程时,只要使用这些组件Componet即可实现相关数据展示及交互等,而 Component是较高层次的抽象,每一个组件在渲染render的时候,都会依次通过Element、DOM来生成最终的页面效果。
在使用NUI开发的应用程序中,组件Component是最高层次的抽象,是直接给用户使用的,NUI.Element是NUI的底层API,主要是由NUI或自定义组件调用的,而DOM是W3C标准定义的原始API,NUI的Element通过操作DOM 来实现页面的效果显示。
在NUI中,组件渲染以后可以通过访问组件的getEl()方法来得到组件对应的Element,通过访问Element的dom属性可以得到其下面的DOM对象。
另外,我们可以通过NUI类的快捷方法find、getEl、getDom等方法来得组件Component、NUI元素Element及DOM节点。 比如:
var button=new NUI.Button({text : "按钮"});//创建了一个组件Component
button.getEl().hide();//调用Element的hidey方法
button.getDom().title = "这是按钮";//通过Element的dom属性操作DOM对象
NUI组件库提供强大的component基类进行组件二次开发 以下例子是创建一个img组件:
var Img = Object.createClass("demo.Img");
Object.mix(Img, Component, true);
这样Img组件就具有最基本的组件特性
Objext.extend(Img, {
_template : "<img src='#{src}'>"
});
这个是组件的html结构模板, 支持模板的变量替换
Objext.extend(Img.prototype, {
_getData : function(oConf){
return {
src : oConf.src || "about:blank"
};
}
});
Objext.extend(Img.prototype, {
setSrc: function(sUrl){
this.getEl().dom.src = sUrl;
}
});
Objext.extend(Img, {
_events : ["srcchanged"]
});
Objext.extend(Img.prototype, {
setSrc: function(sUrl){
var sSrc = this.getEl().dom.src;
this.getEl().dom.src = sUrl;
if(sUrl != sSrc){
this.fire("srcchanged");
}
}
});
这个自定义组件的创建和方法调用,创建一个Img对象:
var img = new Img({autoRender : true});
img.listen("srcchanged", function(){
alert("src changed.");
});
img.setSrc("http://xxx/xx.jpg");
以上是一个非常简单的组件开发和调用的过程,在此过程中,component还提供其他其他丰富的接口进行二次组件开发,具体可以参考极速4.0的组件的代码。
Framease 是一个opoa(one page one application)的实现,使用Framease可以简单快速的开发基于页面模块的ajax应用。
One Page One Application单页面应用(OPOA或1P1A),也就是说,一个页面就是一个应用系统,整个系统只有一个页面,不再使用iframe, 页面提交不能再使用submit方式。这样的好处是:页面上的资源比如JS,CSS,HEAD等整个系统都只需加载一次。加快响应速度。客户体验也有所提高,不再弹出窗口,不再整个页面进行刷新。
在传统的ajax应用中,要开发基于页面模块的应用,会有以下问题:
a) 没有一个可以系统的管理和维护基于页面模块的框架。
b) 由于没有系统的页面模块管理,OPOA的页面都是动态生成html dom,会导致内存泄露的问题。
c) 缺少一个快速,高效的开发和维护模式,由于js脚本的弱类型的特性,造成到了后期的开发和维护代价越来越高,效率也越来越低。
对于以上的问题,Framease框架提供给开发人员一个很好的解决方案,可以方便快速实现基于opoa的系统。
首先说明页面模块默认配置的属性和方法
/** 模块名 */
name : "",
/** 模块是否已载入 */
isLoaded : false,
/** 模块的容器对象 */
container : null,
/** 是否使用cache */
useCache : true,
/** 模块的名称 */
text : "",
/** 获取该模块的初始化数据方法 */
getData : null,
/** 获取该模块的html方法 */
getHtml : null,
/** 显示模块缓存时调用 */
cache : function(){},
/** 初始化方法, 在模块创建后调用 */
init : function(){},
/** 在该模块里进入该模块时调用 */
reset : function(){},
/** 离开该模块前调用, 返回true允许离开, 否则不允许离开 */
exit : function(){return true}
当需要配置可克隆的模块时, 必须配置一个类, 类必须从netease.mail.framease.ModuleClass上面继承, 类的prototype对象为以上配置, 然后需要在类对象配置clone方法, 另外也需要把name属性改为在类对象配置, 所以这里可以有两种方式配置页面模块, 以下是以上配置方式的例子:
var ObjectConfig = {
name : "Welcome",
getHtml : function(){
return "这是配置对象定义的页面模块返回的html";
}
}
var ClassConfig = Object.createClass("demo.ClassConfig");
Object.mix(ClassConfig, netease.mail.framease.ModuleClass,{
clone : function(){
},
name : "ClassConfig"
}, {
getHtml : function () {
return "这是配置模块类方式返回的html";
}
});
除了上面所提到的两种方式配置模块, 还有一种是和loader结合动态请求加载模块文件, 即极速4.0所使用的模块加载方式, 这个扩展的类对loader支持部分代码为以下
(function () {
var ModuleManager = netease.mail.framease.ModuleManager;
var JS4ModuleManager = Object.createClass("netease.mail.jsstyle.core.JS4ModuleManager");
Object.inherit(JS4ModuleManager, ModuleManager, {
get : fNetEaseJS4Get
});
function fNetEaseJS4Get(sModule, oParam){
var Module,
oModules = this.modules,
oMod = oModules[sModule];
if(!oMod || oMod.clone){
try{
Module = oMod || netease.mail.base.Loader.load("module." + sModule, this);
}catch(exp){
return exp;
}
}else{
Module = oMod.constructor;
}
JS4ModuleManager.superClass.get.call(this, sModule, oParam, Module);
}
})();
创建页面模块管理器
JS4 = new netease.mail.framease.ModuleManager([object or class of page module], 页面模块容器id或者对象);
对象配置和类配置的framease例子