YUI杭州什么时候下雪来杭州

前言:由于这段时间公司的事情比较的繁忙,每天都要学习新的东西,所以好久没有写博客了,今天抽空写写博客来记录我这段时间研究的东西,给自己加深一下印象,我用easyUI的组件开发了一个简单操作数据库的小样例,一些常用的数据库操作都实现了,希望能和博客园里面的博主们交流交流意见。首先我用的开发环境是Visual Studio 2010,ASP.NET MVC2.0,SQL SERVER2005,我用的NHibernate的版本是1.2.1,Spring.net的版本是1.1.0。首先在这里将我的疑惑先说出来,希望有人能够给我解答解答,在这里谢过了,我在用版本的时候我发现NHibernate1.2.1和spring.net1.1.0(低版本)结合最好,Nhibernate3.0和spring.net 1.3.1(高版本)结合做好,不知道我的认为正确吗?但是低版本的话不能使用Nhibernate.Linq,只有Nhibernate3.0以上的版本支持Nhibernate,但是高版本的话我们项目里面有一个功能我们没有做出来,就是我们故意把数据库的连接字符串写错误,然后我执行不经过数据操作的页面的话,比如这个页面我就输出一句话,但是也会报错,说无法连接数据库,用户登录失败,但是我想要的是在没有操作数据库的页面可以正常显示,在操作数据库的页面在提示错误即可?不知道哪位博友有什么好方法呢?这种效果在低版本下面我可以实现了。第一步:建立数据库(1)&&& 打开数据库SQL Server 2005,新建一个数据库bjk241,然后再此数据库中新建一张表,起名为:User_DepInfo,用来存放我们录入的数据,表的建立样式如下图所示:&第二步:建立项目,类库等(1) 打开Visual Studio 2010集成开发环境,在Visual Studio 2010中建立一个ASP.MVC 2.0的项目,给项目其名称是NewKJ241,将会生成微软封装好的自带的MVC的整个架构结构,然后在项目里面建立几个类库,用来存放我在后面建立的一些项目,当我们建立完成之后如图所示:&(2) 当我们看到这张图的时候,我们是不是在疑惑这都是干什么的呢?不要着急,下面我就介绍一下各个项目和类库的作用。& 1) 首先我们看到了NewKJ241里面的内容和我们刚建立的不一样吧,那是因为我重新建立了几个文件夹,将没有的文件夹介绍一下,Configs文件夹里面放的是spring.net的&,easyUI文件夹里面是存放的是我们需要引用的easyUI的类库,其他的都是建立的时候自动生成的,后面会讨论的。& 2) NewKJ241.Model类库的作用是存放映射的数据库的文件。& 3) NewKJ241.IDao类库的作用是实现了我们所需要的在项目中实现数据库方法的接口。& 4) NewKJ241. NhibernateDao类库的作用是实现了IDao的接口,完成了接口所要实现的方法的代码。& 5)NewKJ241.IBLL类库的作用是实现了我们在项目中要使用Spring.NET所要使用的接口,和NewKJ241.IDao接口的写法一模一样。& 6)NewKJ241.BLL类库的方法是实现了Nhibernate的实现方法,继承自IBLL项目,是为了使用Spring.NET所写的方法。下面我就讲我写的东西详细的讲述一下,有什么不足或者缺点我们可以讨论。第三步:NHibernate映射文件(1) 首先我们要映射出数据库中表的信息的文件,这里我们怎么映射呢,那么多的代码??别着急,我们可以用一个自动生成映射文件的组件CodeSmith软件,这个软件可以快速的帮助我们生成映射文件,关于这个软件怎么使用可以看我以前写的篇博客,那里详细的叙述了如何使用这个软件:/hanyinglong/archive//2462302.html。(2) 接下来我们在NewKJ241.Model里面建立一个文件夹Mappings,在这个文件夹里面我们存放了映射的文件,生成之后附加到项目中如图所示:&1)& UserDepInfo的作用是定义数据库中的字段,再用CodeSmith生成的时候模式字段属性是不加Virtual的,我们人为的给字段属性加上,代码如下:&&&&&&& protected int _&&&& &&&protected int _depID;&&&&&&& protected string _depN&&&&&&& public UserDepInfo() { }&&&&&&& public UserDepInfo(int depID, string depName)&&&&&&& {&&&&&&&&&&& this._depID = depID;&&&&&&&&&&& this._depName = depN&&&&&&& }&&&&&&& public virtual int Id&&&&&&& {&&&&&&&&&&& get { return _ }&&&&&&&&&&& set { _id = }&&&&&&& }&&&&&&& public virtual int DepID&&&&&&& {&&&&&&&&&&& get { return _depID; }&&&&&&&&&&& set { _depID = }&&&&&&& }&&&&&&& public virtual string DepName&&&&&&& {&&&&&&&&&&& get { return _depN }&&&&&&&&&&& set&&&&&&&&&&& {&&&&&&&&&&&&&&& if (value != null && value.Length & 50)&&&&&&&&&&&&&&&&&&& throw new ArgumentOutOfRangeException("Invalid value for DepName", value, value.ToString());&&&&&&& &&&&&&&&_depName =&&&&&&&&&&& }&&&&&&& }2) 然后映射文件的代码如下:注意,在这里我们需要把这个/hanyinglong/archive//2459314.html。&?&hibernate-mapping &&&&&&&&&&&&&&&&&& assembly="NewKJ241.Model.UserDepInfo"&&&&&&&&&&&&&&&&&& namespace="NewKJ241.Model.UserDepInfo"&& &class name="NewKJ241.Model.UserDepInfo,NewKJ241.Model" table="User_DepInfo" lazy="false"&&&& &!--下面是使用缓存所使用的一句话,但是本项目里面的二级缓存注释掉了,所以这里也注释--&&&& &!--&cache usage="read-write"/&--&&&& &!--部门表主键ID--&&&& &id name="Id" type="Int32" unsaved-value="null"&&&&&& &column name="ID" length="4" sql-type="int" not-null="true" unique="true"/&&&&&& &generator
/&&&& &/id&&&& &!--部门表ID--&&&& &property name="DepID" type="Int32"&&&&&& &column name="DepID" length="4" sql-type="int" not-null="false"/&&&& &/property&&&& &!--部门表名称--&&&& &property name="DepName" type="String"&&&&&& &column name="DepName" length="50" sql-type="varchar" not-null="false"/&&&& &/property&& &/class&&/hibernate-mapping&第四步:建立Dao接口,实现操作数据库的方法(1) 在NewKJ241.IDao类库下面新建一个类,起名为:IUserDepInfoDao,然后在此类中写要实现方法的接口,代码如下:& //where表明了对类型变量T的约束关系,where T : class表示类型变量T是继承自class或者class本身&&& public interface IUserDepInfoDao&T& where T : class&&& {&&&&&&& /// &summary&&&&&&&& /// 获取实体&&&&&&& /// &/summary&&&&&&&& /// &param name="id"&主键&/param&&& &&&&&/// &returns&实体&/returns&&&&&&&& T Get(object id);&&&&&&&& /// &summary&&&&&&&& /// 获取实体&&&&&&& /// &/summary&&&&&&&& /// &param name="id"&主键&/param&&&&&&&& /// &returns&实体&/returns&&&&&&&& T Load(object id);&&&&&&&& /// &summary&&&&&&&& /// 插入实体&&&&&&& /// &/summary&&&&&&&& /// &param name="entity"&实体&/param&&&&&&&& /// &returns&插入的数据&/returns&&&&&&&& object Save(T entity);&&&&&&&& /// &summary&&&&&&&& /// 修改实体&&&&&&& /// &/summary&&&&&&&& /// &param name="entity"&实体数据&/param&&&&&&& &void Update(T entity);&&&&&&&& /// &summary&&&&&&&& /// 保存或者修改实体&&&&&&& /// &/summary&&&&&&&& /// &param name="entity"&实体数据&/param&&&&&&&& void SaveOrUpdate(T entity);&&&&&&&& /// &summary&&&&&&&& /// 删除实体&&&&&&& /// &/summary&&&&&&&& /// &param name="id"&主键ID&/param&&&&&&&& void Delete(object id);&&&&&&&& /// &summary&&&&&&&& /// 删除实体&&&&&&& /// &/summary&&&&&&&& /// &param name="odList"&主键ID的集合,删除多个项&/param&&&&&&&& void Delete(string idList);&&&&&&&& /// &summary&&&&&&&& ///读取如果用户不输入任何信息进行查询的方法&&&&&&& /// &/summary&&&&&&&& /// &param name="sort"&按照某个字段进行排序&/param&&&&&&&& /// &param name="order"&按照某个字段进行排序&/param&&&&&&&& /// &returns&&/returns&&&&&&&& IList&UserDepInfo& loadByAll(string sort, string order);&&&&&&&& /// &summary&&&& &&&&/// 读取用户输入条件进行查询的方法&&&&&&& /// &/summary&&&&&&&& /// &param name="sort"&按照某个字段进行排序&/param&&&&&&&& /// &param name="order"&按照某个字段进行排序&/param&&&&&&&& /// &param name="name"&只用户输入的信息&/param&&&&&&&& /// &returns&&/returns&&&&&&&& IList&UserDepInfo& loadAllCheck(string sort, string order, string name);&&&&&&&& /// &summary&&&&&&&& ///&&&&&&& /// &/summary&&&&&&&& /// &param name="total"&总的数量&/param&&&&&&&& /// &param name="page"&当前的页数&/param&&&&&&&& /// &param name="rows"&每页所显示的数量&/param&&&&&& &&/// &param name="order"&按照某个字段进行排序&/param&&&&&&&& /// &param name="sort"&按照某个字段进行排序&/param&&&&&&&& /// &param name="DepName"&只用户输入的信息&/param&&&&&&&& /// &returns&&/returns&&&&&&&& IList&UserDepInfo& LoadAllByPage(out long total, int page, int rows, string order, string sort, string DepName);}这里就没什么说的了,写法都很固定!!第五步:建立NhibernateDao项目,实现IDao接口的方法(1) 在NewKJ241.Nhibernate类库下面建立类,起名为:UserDepInfoDao,在该方法中继承自IDao接口,实现了IDAO接口里面的所有方法,在书写本代码的时候,我们必须引入dll,引入的dll如图所示:&代码如下:&public class UserDepInfoDao&T& : HibernateDaoSupport, IUserDepInfoDao&T& where T : class&&& {&&&&&&& public T Get(object id)&&&&&&& {&&&&&&&&&&& return this.HibernateTemplate.Get&T&(id);&&&&&&& }&&&&&&&& public T Load(object id)&&&&&&& {&&&&&&&&&&& return this.HibernateTemplate.Load&T&(id);&&&&&&& }&&&&&&&& public virtual object Save(T entity)&&&&&&& {&&&&&&&&&&& return this.HibernateTemplate.Save(entity);&&&&&&& }&&&&&&&& public void Update(T entity)&&&&&&& {&&&&&&&&&&& this.HibernateTemplate.Update(entity);&&&&&&& }&&&& &&&&public void SaveOrUpdate(T entity)&&&&&&& {&&&&&&&&&&& this.HibernateTemplate.SaveOrUpdate(entity);&&&&&&& }&&&&&&&& public void Delete(object id)&&&&&&& {&&&&&&&&&&& var entity = this.HibernateTemplate.Get&T&(id);&&&&&&&&&&& if (entity == null)&&&&&&&&&&& {&&&&&&&&&&&&&&&&&&&&&&&&&& }&&&&&&&&&&& else&&&&&&&&&&& {&&&&&&&&&&&&&&& this.HibernateTemplate.Delete(entity);&&&&&&&&&&& }&&&&&&& }&&&&&&&& public void Delete(string idList)&&&&&&& {&&&&&&&&&&& string[] idl = idList.Split(',');&&&&&&&&&&& int len = idl.L&&&&&&&&&&& for (int i = 0; i & i++)&&&&&&&&&&& {&&&&&&&&&&&&&&& var entity = this.HibernateTemplate.Get&T&(int.Parse(idl[i].ToString()));&&&&&&&&&&&&&&& if (entity == null)&&&&&&&&&&&&&&& {&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&&&& }&&&&&&&&&&&&&&& else&&&&&&&&&&&&&&& {&&&&&&&&&&&&&&&&&&& this.HibernateTemplate.Delete(entity);&&&&&&&&&&&&&&& }&&&&&&&&&&& }&&&&&&& }&&&&&&&& public IList&UserDepInfo& loadByAll(string sort, string order)&&&&&&& {&&& &&&&&&&&string hql = "from UserDepInfo order by " + sort + " " +&&&&&&&&&&& return this.HibernateTemplate.Find&UserDepInfo&(hql).ToList();&&&&&&&& }&&&&&&&& public IList&UserDepInfo& loadAllCheck(string sort, string order, string name)&&&&&&& {&&&&&&&&&&& string hql = "from UserDepInfo where DepName like ? order by " + sort + " " +&&&&&&&&&&& return this.HibernateTemplate.Find&UserDepInfo&(hql, new object[] { "%" + name + "%" }).ToList();&&&&&&& }&&&&&&&&& public IList&UserDepInfo& LoadAllByPage(out long total, int page, int rows, string order, string sort, string DepName)&&&&&&& {&&&&&&&&&&& IList&UserDepInfo& list =&&&&&&&&&&& if (DepName == null)&&&&&&&&&&& {&&&&&&&&&&&&&&& list = this.loadByAll(sort, order);&&&&&&&&&&& }&&&&&&&&&&& else&&&&&&&&&&& {&&&&&&&&&&&&&&& list = this.loadAllCheck(sort, order, DepName);&&&&&&&&&&& }&&&&&&&&&&& total = list.LongCount();&&&&&&&&&&& list = list.Skip((page - 1) * rows).Take(rows).ToList();&&&&&&&&&&&&&&&&&& }}哎,工作一天了,累了,回学校了,明天继续,今天写的这些都是基础,后面还会在写这个项目的,一直到这个项目写完!!,也是最有知识点了,所谓学习吗,一步一个脚印,慢慢来吧!!!
l1519的资源 - 下载频道 - CSDN.<em ASP MVC+Spring+Nhibernate+EasyUI+Jquery开发案例,运用这几个技术开发的项目 上传者:hanyinglong上传时间: 下载时间: 下载次数:...momocheng的资源 - 下载频道 - CSDN.<em 标签: ASP MVC ASP MVC+Spring+Nhibernate+EasyUI+Jquery开发案例,运用这几个技术开发的项目 上传者:hanyinglong上传时间: 下载时间:...《实战突击:ASP项目开发案例整合(含DVD光盘1张)》明日科技 等... 《实战突击:asp项目开发案例整合(含dvd光盘1张)》以在线播客视频、电子商务平台、ajax校内数码相册、博客管理系统、上在线论坛、企业门户站、城市小区...ASP MVC+Spring+Nhibernate+EasyUI+Jquery开辟案例(2),...我们全部在MVC新建的项目中书写代码,因为这里用到了Spring,Nhibernate,MVC,easyUI等,所以很是值得我们去研究,当然我的写法可能也有题目,欲望我们可以交换,如许...<em教程:Asp MVC 4 Web API 中的安全认证(一) - Nic PeiASP MVC4中的Web API提供了很好的API接口开发方式。可以更好地适应现在的...上一条资料: ASP MVC+Spring+Nhibernate+EasyUI+Jquery开发案例(1) ...mvc2中关于图片上传 - 孙艺玮_<em教程_电脑学(Xue5)点此进一步了解“mvc2中关于图片上传 - 孙艺玮”相关的更多内容 转载请注明〔...一条资料: ASP MVC+Spring+Nhibernate+EasyUI+Jquery开发案例(1) - ...ASP项目开发案例精粹(含DVD光盘1张) 书籍 计算机教材 商城-...所属类目:QQ购 &图书杂志 & 计算机/络 & 程序设计 & 其他开发 & 商品详情新华文轩官方旗舰店 文轩首页| 购买须知| 新手帮助| 留言板| 信用评价| 经济...ASP·<em项目开发案例精粹(含DVD光盘1张)详细介绍及目录 本书通过介绍20个具有代表性的项目实例的开发过程,使读者掌握ASP 3.5中的各种技术在开发中的实际使用。前10个案例详细地讲解了完整的开发过程,后10个案例...《实战突击:ASP项目开发案例整合(含DVD光盘1张)》 - 买旧书 ...《实战突击:ASP项目开发案例整合(含DVD光盘1张)》内容提要: 銆 銆 銆婂疄鎴樼獊鍑伙细asp椤圭洰寮 鍙戞 渚嬫暣鍚?鍚玠vd鍏夌洏1寮?銆嬩互鍦...实战突击:ASP项目开发案例整合(附DVD光盘1张)(明日科技)【...《实战突击:ASP项目开发案例整合》以在线播客视频、电子商务平台、AJAX校内数码相册、博客管理系统、上在线论坛、企业门户站、城市小区物业内部管理、无纸...亮剑ASP项目开发案例导航-含DVD光盘1张 丁士锋 - 蔚蓝|蔚蓝...全书共介绍了10个项目案例,内容涉及目前较为流行的开发框架,如ASP Web Form、AJAX、Silverlight与ASP的整合开发、ASP MVC、jQuery、ExtJs等技术。...ASP基础与案例开发详解(配DVD光盘1张)/易巍, 张新颖, 张孝祥,... 图书 高级搜索 所有分类 最新图书 预售图书 排行榜 教材教辅 少儿 文学 小说 历史 管理 励志 人文社科 进口图书 最新促销 ......ASP MVC+Spring+Nhibernate+EasyUI+Jquery开发案例(2)...我们全部在MVC新建的项目中书写代码,因为这里用到了Spring,Nhibernate,MVC,easyUI等,所以非常值得我们去研究,当然我的写法可能也有问题,希望我们可以交流,这样我们...MVC+Spring+Nhibernate+EasyUI+Jquery - 下载频道 - CSDN.<em标签: ASP MVC 分享到: 新浪微博 腾讯微博下载:进入下载页 VIP下载 ...ASP MVC+Spring+Nhibernate+EasyUI+Jquery开发案例,运用这几个技术开发...yan7294065的资源 - 下载频道 - CSDN.<em ASP MVC+Spring+Nhibernate+EasyUI+Jquery开发案例,运用这几个技术开发的项目 上传者:hanyinglong上传时间: 下载时间: 下载次数:...easyui tree asp mvcASP MVC+Spring+Nhibernate+EasyUI+Jquery开辟案例(1)专业的上海站建设,主营站建设、络开发、站制作、页设计。我们以优质的服务合 …...亮剑ASP项目开发案例导航(附DVD光盘1张):亚马逊:图书</h3集成了越来越多的新功能,如对jQuery、AJAX的内置支持,让开发Web应用程序变得越来越快捷、高效,《亮剑ASP项目开发案例导航》就是结合这些新框架实现项目的开发...《ASP 3.5动态站开发案例指导(附CD光盘1张)》(王改性,等)【...图文 《ASP 3.5动态站开发案例指导》共19章,系统地介绍了如何使用ASE<em开发...导航、AJAX、MVC等技术讲解,最后通过三个综合实例展示了如何使用多种技术来开发...china1314920的资源 - 下载频道 - CSDN.<em ASP MVC+Spring+Nhibernate+EasyUI+Jquery开发案例,运用这几个技术开发的项目 上传者:hanyinglong上传时间: 下载时间: 下载次数:...《ASP项目开发案例精粹(附DVD光盘1张)》(炎士涛,等)【摘要 ... 《ASP项目开发案例精粹(附DVD光盘1张)》介绍了20个不同行业的动态站... 锋利的jQuery(第2版) 锋利的jQuery(第2版) ¥34.30(7.0折) 程序员...第1章 供求信息(一)(17) - 第1章 供求信息(一) - ASP项目...您所在的位置:在线读书 && 亲子·教育 && ASP项目开发案例全程实录(第2版) && 第1章 供求信息(一)在线阅读《ASP项目开发案例全程实录(第2版)》...第1章 供求信息(一)(21) - 第1章 供求信息(一) - ASP项目...您所在的位置:在线读书 && 亲子·教育 && ASP项目开发案例全程实录(第2版) && 第1章 供求信息(一)在线阅读《ASP项目开发案例全程实录(第2版)》...《实战突击:ASP项目开发案例整合(附DVD光盘1张)》【摘要 书评... 《实战突击:ASP项目开发案例整合》以在线播客视频、电子商务平台、AJAX校内数码相册、博客管理系统、上在线论坛、企业门户站、城市小区物业内部管理、无...第1章 供求信息(一)(14) - 第1章 供求信息(一) - ASP项目...您所在的位置:在线读书 && 亲子·教育 && ASP项目开发案例全程实录(第2版) && 第1章 供求信息(一)在线阅读《ASP项目开发案例全程实录(第2版)》...第1章 供求信息(一)(10) - 第1章 供求信息(一) - ASP项目...您所在的位置:在线读书 && 亲子·教育 && ASP项目开发案例全程实录(第2版) && 第1章 供求信息(一)在线阅读《ASP项目开发案例全程实录(第2版)》...第1章 供求信息(一)(34) - 第1章 供求信息(一) - ASP项目...您所在的位置:在线读书 && 亲子·教育 && ASP项目开发案例全程实录(第2版) && 第1章 供求信息(一)在线阅读《ASP项目开发案例全程实录(第2版)》...spring nhibeate mvc本文标题:Spring MVC 3.2 将支持 Servlet 3 异步请求 本文: ...ASP MVC+Spring+Nhibernate+EasyUI+Jquery开辟案例 ......第1章 供求信息(一)(29) - 第1章 供求信息(一) - ASP项目...您所在的位置:在线读书 && 亲子·教育 && ASP项目开发案例全程实录(第2版) && 第1章 供求信息(一)在线阅读《ASP项目开发案例全程实录(第2版)》...第1章 供求信息(一)(30) - 第1章 供求信息(一) - ASP项目...您所在的位置:在线读书 && 亲子·教育 && ASP项目开发案例全程实录(第2版) && 第1章 供求信息(一)在线阅读《ASP项目开发案例全程实录(第2版)》...第1章 供求信息(一)(35) - 第1章 供求信息(一) - ASP项目...您所在的位置:在线读书 && 亲子·教育 && ASP项目开发案例全程实录(第2版) && 第1章 供求信息(一)在线阅读《ASP项目开发案例全程实录(第2版)》...第1章 供求信息(一)(5) - 第1章 供求信息(一) - ASP项目...您所在的位置:在线读书 && 亲子·教育 && ASP项目开发案例全程实录(第2版) && 第1章 供求信息(一)在线阅读《ASP项目开发案例全程实录(第2版)》...第1章 供求信息(一)(37) - 第1章 供求信息(一) - ASP项目...您所在的位置:在线读书 && 亲子·教育 && ASP项目开发案例全程实录(第2版) && 第1章 供求信息(一)在线阅读《ASP项目开发案例全程实录(第2版)》...第1章 供求信息(一)(8) - 第1章 供求信息(一) - ASP项目...您所在的位置:在线读书 && 亲子·教育 && ASP项目开发案例全程实录(第2版) && 第1章 供求信息(一)在线阅读《ASP项目开发案例全程实录(第2版)》...D2之淘宝技术沙龙 是与淘宝技术嘉年华共同组织协办,是一个倡导“享技术 聚朋友”的互动、交流的平台。
杭州站内容
本期主题:《前端周边经验大家谈》
沙龙时间:日13:30-18:00
沙龙地点:浙江省人民大会堂o多功能厅(省府路9号)
报名地址:
分享话题介绍
话题1:《前端测试在路上》
简介:前端测试一直是混沌状态,认识上不一致,各家也都在尝试;前端测试究竟应该包含哪些范畴,如何提升产品的可测试性,如何根据产品制定测试方案,和大家一起分享前端测试路上的曲折和思考.
淘宝网 赵勇(前端技术专家)
嘉宾简介:现任淘宝网交易线前端负责人,今年比较关注产品的可维护性,努力推动前端开发的专业化
话题2:《更好的文件组织方式》
简介:随着前端项目复杂度的提高,文件的组织方式直接关系到代码的可维护性。好的组织方式可以降低复杂性,提高效率。而坏的反而会增加麻烦。
豆瓣网 张克军 (执着的前端工程师)
嘉宾简介:豆瓣前端架构师,贪玩前端技术,把Hack当成是一种生活态度,积极前端布道。
话题3: 《在Web中实现ios风格的手势和动画效果》
简介:在ios平台上,苹果以它流畅人性化的交互和UI效果而征服了大量的用户。在这个话题中,我将会把ios上的交互和UI效果拆解成小功能点,并且探讨其设计与实现方式。本话题主要包含两个部分:第一部分是利用touch事件来实现ios中常见的gesture。第二部分是利用HTML5的transition和transform来实现ios风格的缓动动画。
盛大创新院 程劭非(winter,盛大创新院研究员)
嘉宾简介:曾参与Bambook开发,更早为微软工作,开发Windows CE平台的IE浏览器。技术Blog是,上@寒冬winter
前端的职业发展
前端测试在项目的价值
前端的数据监控的方式与实现
html5 在项目中的应用
北京站内容
以前我们将精力过多放在技术知识的体系化,工具的自动化,WPO等技术方向,当前随着HTML5技术的推广以及移动终端应用市场的兴起,我们手上另又拥有了另一把利剑,技术的价值体现在业务的展现之上,前端技术的价值更多的体现在业务的展现层,所以,如何把我们手上这些技术转换为一种驱动力去影响产品甚至驱动业务,如何在一个产品从无到有的过程中体现前端的价值,这些将是本期沙龙讨论的热点。
本期我们主要选择在线旅游市场为案例,结合在线旅游网站的业务模式多样化,重度依赖前端技术且需求多变的特性,邀请目前国内几大在线旅游网站的前端负责人讲解他们在各自的业务线如何展开前端工作,常遇到的挑战,以及如何影响产品的需求和设计。
本期主题:《前端技术驱动力&在线旅游》
沙龙时间:日13:30-18:00
沙龙地点:京仪大酒店o第一会议室(海淀区大钟寺东路9号)
报名地址:
分享话题介绍
话题1:《高粒度模块化的前端开发》
简介:多人Web开发场景中,繁琐的业务逻辑,复杂的产品功能,高耦合的UI表现,给前端开发团队带来的挑战,该如何应对?我将与您分享在多人项目中高颗粒化拆分、抽离表现和功能的方法,以及组织、调用模块的技术方案;介绍这种解决方案在ChinaFace2.0前端开发过程中实践和经验。
ChinaFace 蒋吉兵(前端开发负责人)
嘉宾简介:
那些年,我(们)一起开发雅虎首页、邮箱、关系;
这些年,我(们)共同折腾淘宝、银泰、ChinaFace;
我钟爱YUI,更崇尚高效协同的前端开发
@集冰 一直在路上的前端人
话题2:《淘宝旅行大促》
简介:每年都有一次淘宝旅行大促,带来了海量的流量,秒杀引入了瞬间峰值,如何运用前端技术对性能进行优化,降低服务器压力并保证用户抢票的准确率,如何应对大促当天可能出现的种种突发情况,如何做备份方案,逐一展开。
淘宝网 陈良 (花名舒克,淘宝旅行前端负责人)
嘉宾简介:2010年加入淘宝旅行前端团队,负责淘宝旅行前端重构以及前端新技术在旅行线的推广。
话题3:《前端性能优化及开发工具》
简介:去哪儿网的大多数应用均重度依赖前端,所以性能成为影响用户体验的重中之重。 我将分享我们在性能调优方面的一些经验。 另外介绍一下去哪儿网前端组使用的开发辅助工具。
去哪儿 林浩(前端负责人)
嘉宾简介:2008年加入,负责前端研发工作。专注于前端基础构建,性能优化及开发辅助工具。
如何利用前端技术驱动旅游业务线各类产品和设计
搜索结果页面的前端架构和性能优化;
前端如何满足日益兴起的移动终端需求导语:这篇“基于YUI3开发web应用的诀窍”是比较经典的介绍 YUI3 工作机制的文章,文章发布在上,总体难度适中,比较适合初学者认识、了解 YUI3。以此纠集了三名应届同学来翻译这篇文章:函谷、郝黎、张勉,并希望能对正在学习YUI3的同学有所帮助和启发。
原文标题:A Recipe for a YUI 3 Application
原文地址:
译文:使用YUI 3开发Web应用的诀窍
我们知道,YUI3的设计始终围绕着“模块”展开。今天我不想过多解释什么是模块,因为在他的文章&#8221;&#8220;中已经做了详尽说明。在这里我将着重阐述如何构建这些模块。文章中的大部分内容都可以从在线文档查阅,并有其他可代替的方法。当然在线API文档的内容大而全。本文只是介绍其中的精华的部分——基于YUI3开发web应用的诀窍,这里的“诀窍”更针对短小精悍的程序场景,不像Nicholas Zakas所指的架构级场景,毕竟仅凭本文的篇幅无法全部展开讲述YUI3。
模块的定义
首先我们要对模块进行定义,一种常见的方法是将页面可视区域的不同部分做切分,导航、菜单、正文、边栏面板等等。然后查一下YUI是否已经提供了这些模块,比如YUI3就没有提供“菜单”组件,但提供了Node-MenuNav插件,这个插件可以将结构化好的html代码(ul&li)渲染成具有交互行为的菜单。或者你可以直接去YUI Gallery中去找基础组件。不管怎样,你总会找到一种容器或者布局,可以让你往里填充你需要的东西,ok,让我们从这里开始。
我建议将每个模块都封装进一个文件,放在和文件名同名的目录中,比如weather模块应当放在/weather/weather.js中,这样做的原因是,有可能你的组件会依赖一些样式,包括css和img等,将这些样式和资源文件放到和js同一个目录下,YUILoader就会很方便的找到他们。这样,样式文件就可以放在weather/assets/skins/sam/weather.css,同样,其他图片和样式也可以按这种方式放置,当然我们假设你没有使用YUI Builder来打包你的项目,这就有点说来话长了。assets目录和skin目录的含义不言自明,但sam目录就搞不懂啥意思了,其实sam是YUI配置项中skin的默认值,指代YUI内嵌组件的默认样式,sam取名自其设计师Sam Lind。因此你也可以使用你的昵称作为你的组件皮肤名称,当然这需要你在YUI全局配置中传入skin参数,简单起见,我们这里只使用默认皮肤。
模块文件模板
这里是一段最常用的模块定义的代码:
/*jslint devel: true,
undef: true, newcap: true, strict: true, maxerr: 50 */
/*global YUI*/
* The module-name module creates the blah blah
* @module module-name
YUI.add(‘module-name’, function (Y) {
"use strict";
// handy constants and shortcuts used in the module
var Lang = Y.Lang,
CBX = ‘contentBox’,
BBX = ‘boundingBox’,
NAME = ‘xxxx’;
* The Xxxx class does ….
* @class Xxxx
* @extends Widget
* @uses WidgetParent
* @constructor
* @cfg {object} configuration attributes
Y.Xxxx = Y.Base.create(
[Y.WidgetParent],
// Instance members here
// Static members here, specially:
}, ’0.99′, {
requires: ['widget','widget-parent'],
skinnable: true
前两行是供过滤这段代码用的,是我非常推荐的一个语法检查工具。在网页版本中,会有一个选项区来设置选项,这排在页面的底部,如果你使用,会自动去读取注释中的配置,来依此对你的代码进行语法检查。
接下来的注释是为了生成,这会为你省去不少麻烦,文档有固定格式的模板,你只要通过注释语法填充内容就可以了。当项目越来越大,你就会越来越依赖这种项目文档。
现在开始真正的代码段,语句。我们用来告知模块名称、模块内容、以及其他的一些信息。模块名通常使用小写字母加中横线组成,这些模块名称就是你在文档左侧看到的名字列表。当然你也看到有很多不是小写字母加中横线的命名,毕竟这种命名方法并非强制,你可以根据你的习惯做模块命名。
的第二个参数是一个带入实例的函数,这个实例通常被命名为,这个回调中的逻辑即是模块的主体部分,而你需要依赖的模块的功能方法都会被挂载到这个上。跳到代码的结尾,你会看到剩下的参数,版本号(也可以不需要)和模块配置项,模块配置项是一个对象,这里代码的含义就是告诉这个模块依赖了和,并包含皮肤。当然,在这个例子中是多余的,因为依赖了,这时即便你写上,也不会重复加载。而且你根本不必在意模块的顺序,会计算他们的依赖关系并做好排序。你也会注意到中包含一个方法,的功能就和它很类似。
在函数体内,首先是“的声明,这个声明源自,这使你的代码对于未来的解释器也具备足够好的适应能力,对于旧的解释器也只相当于定义了一个孤零零的字符串值而已,不妨碍程序运行。“声明指定了一个函数作用域,因此将这句声明放在内比放在文件最顶部要更安全一些,因为如果将其放在全局,这回影响后续加载的所有逻辑。
然后是定义常量和变量的缩写,本身没有常量的概念,这里的“常量”只是一种约定,和在其他编程语言中一样,常量通常使用大写字母和下划线来命名。这样做的好处有两个,特别是字符串常量,一是当你多次写一个字符串的时候,如果字符串拼错则只能在运行时报错,当改为常量形式如果出错在语法检查时就能发现。二是让工作效率更高,使用常量可以节省不少字节数。通常,我们更多的把配置属性和事件名称定义为常量。
缩写,比如用代替,写的更少,解释器执行更快直接使用引用而不是逐级查找对象成员。
在所需的文档注释后是实际的代码,我们需要将自己的模块对象挂载在上, 比如 。我建议采用来创建模块,这样能创建的派生类(即派生自),模板基本能满足大多数的模块功能需求,因此不必在用其他方式来代替。其中的第一个参数是模块名称,属性是的一个描述,通常用驼峰命名法,这个名字会用于事件的前缀(例如”),抑或是组件容器的命名前缀(例如),此外,还是执行“实例时的默认返回值,在模式下经常看到很多类似的。这里我们使用常量来标示类模块的名称。
的第二个参数是要派生的父类。比如,你会使用作为无组件的父类、使用作为带组件的父类、作为插件的父类、或者使用任意派生自的子类作为父类,也就是说,任意通过创建的类都可以再次通过被继承。
第三个参数是一个参元类,摻元类的方法都将挂载到你的类上,常用的参元类包括从派生来的、从派生来的类似的子模块、和(这两个参元类已经被继承),中的继承机制是如此强大,以至于你看的源码仅仅是一句从的继承。子类可以继承自多个父类,因此第三个参数是一个数组。
最后是实际的代码实现部分。第四个和第五个参数都是对象,它们包含了实例,和类中的静态成员。实例属性通过深拷贝挂载到类的原型(prototy)上,通过this来访问他们,其他静态成员则是可公开访问的。
静态成员中最重要的就是ATTRS,其中列出了你的类所需的所有配置参数。例如你需要一个名为value的成员,这个成员用于保存一个数字,默认初始值为0,可以这样传入第五个参数。
validator: Lang.isNumber
ATTRS中当然可以写入多个成员,每个成员都下辖各自的配置项,通过阅读来了解更多参数配置。我们看到,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 ];
除非函数足够简单,最好单独重写setter、setter和validator,传入他们的函数名作为实参,让Attribute将函数名对应到真实的函数。
总之,使用setter作为标准输入,这样会为组件带来一些非常有用的特性。比如配置项值的改变会触发beforeChange和afterChange事件,如果在beforeChange事件中停止事件,则可以阻止属性的更改,因此组件实例生成后,请善用自定义事件。比如你如果绑定了afterChange事件,如果触发了回调,那么一定是值被改变了,而不是即便试图更改属性值却在beforeChange中被阻止了。
validator和setter的校验强度是你来决定的,如果你的validator的校验逻辑很严格,就不用再在setter中添加严格的校验,甚至不需要setter,反之亦然,setter校验很严格的话也可以缺省validator。比如,下面两句代码,你可以任选其一:
validator: Y.Lang.isBoolean,
// to make the attribute accepts strictly a Boolean
setter: Boolean, // to make the attribute accept any value and have Boolean turn it into one
Setter也可以作为校验器来使用。如果输入的值不能转换成attribute可接受的值,setter就会返回Y.Attribute.INVALID_VALUE。这种情况下,attribute不会发生改变。
当我定义成员的时候,通常会指定这个常量,并放在模块的顶部(和CBX, BBX这些缩写声明一起),例如:
var VALUE = ‘value’,
VALID_CODES = ‘validCodes’;
这样做的原因是,在一个模块内可能会多次用到这个值,使用常量更加方便。但是,请千万别在属性声明中使用这些常量。
// *** Don’t do this *** //
validator: Lang.isNumber
这样书写会生成一个名为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使用指南中的来了解更多。
当然,你有时会提供一些类静态成员(属性/方法),并暴露给开发者去调用,而类中的常量由于闭包的保护对外并不可见,如果你想对外暴露他们,则需要通过Y.Base.create()的第五个参数传入一个boject,object的成员都会被当作静态成员挂载在类本身上。例如中的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上(参见),也可以给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& 而不是&div&&div&&/div&&/div&结构,其实Widget不强制要求使用一个大容器(contentBox),这两个属性可以使用任意html标签,比如我用a来描述按钮
BOUNDING_TEMPLATE: ‘&a&’,
CONTENT_TEMPLATE: null,&/a&
在你不需要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: function () {
var cbx = this.get(CBX);
cbx.append(Y.Node.create(Y.substitute(Y.Xxxx.TEMPLATE, CLASS_NAMES)));
这段代码的运行需要特定的上下文,首先,要声明常量CBX(见本篇文章第一个代码块),其次,widget所需的模块都加载完全,加载widget之前会自动加载其依赖的Node, Y.substitute是可选模块,如果需要则要把’substitute’ 加入到requires中。此外还要有一个作为widget内容模板的名为TEMPLATE的静态变量,你可以和其它静态成员一起定义(通过ATTRS定义)它。最后常量CLASS_NAMES应当也已经事先定义好了。
我通常在模块开头,和BBX,CBX一起定义CLASS_NAMES(见本文第一个代码块):
var BBX = ‘boundingBox’,
CBX = ‘contentBox’,
NAME = ‘button’,
// other constants and shortcuts ….
YCM = Y.ClassNameManager.getClassName,
getClassName = function () {
var args = Y.Array(arguments);
args.unshift(NAME);
return YCM.apply(this, args).toLowerCase();
LABEL = ‘label’,
PRESSED = ‘pressed’,
ICON = ‘icon’,
CLASS_NAMES = {
pressed: getClassName(PRESSED),
icon: getClassName(ICON),
label: getClassName(LABEL),
noLabel: getClassName(‘no’, LABEL)
CLASS_NAME为一个object常量,其中的属性值由ClassNameManager(包含在Widget中)生成。在以上代码中,我先声明一个指向Y.ClassNameManager.getClassName的缩写YCM,然后声明一个只能在模块内部访问的私有函数getClassName,这个函数的功能与Widget中的同名函数的功能很类似,只是widget中的getClassName是个静态函数,可以用来生成静态变量。CLASS_NAMES中表示样式名的属性值就是用它生成的。现在,我就可以这样定义TEMPLATE中的样式名了:
TEMPLATE: ‘&label&’,&/label&
这还不够,我还想将其它的值比如配置属性中的值加入模板中。可以这样做:
this.get(CBX).append(Y.Node.create(Y.substitute(TEMPLATE , CLASS_NAMES, Y.bind(function (key, suggested, arg) {
return (key === ‘_’?this.get(arg):suggested);
},this))));
我给Y.substitute增加了第三个参数,一个函数。通常,Y.substitute中的占位符是由一对花括号包围的字符串,如果其中有空格,占位符就会被一分为二。空格前面的部分为key,空格后的部分为可选参数。当Y.substitute的第三个参数是一个函数时,这个特性十分有用。比如在此例中,第一个参数是key,第二个参数是用于替换的内容,此例中是CLASS_NAME,第三个是可选参数。所以在上面的语句之后,我可以这样使用模板:
TEMPLATE: ‘&label for="”{_" /&’,
Y.substitute在执行时先碰到{label},开始在CLASS_NAME中搜索,并找到对应值‘yui3-button-label’。然后以‘label’, ‘yui3-button-label’ 和 undefined为参数调用替换函数。因为key不等于’_’,所以函数返回第二个参数中值,即原始的样式名。当遇到{_ id}时,因为在CLASS_NAMES中没有’_’属性,所以会以传入参数‘_’, undefined 和 ‘id’调用替换函数,函数会从配置属性中读取’id’的值。在遇到占位符{_ value}时函数将执行同样的操作。
所有在模块的代码顶部声明的常量在模块外都是不可见的,但你可能想使一些常量可见,比如CLASS_NAMES。此时,你只需在Y.Base.create的最后一个参数中,即声明静态成员的区域声明:
CLASS_NAMES: CLASS_NAMES
这样这一对象及其包含的所有属性都可通过Y.MyWidget.CLASS_NAMES来访问。
我建议尽可能将widget的HTML字符串都调整好再执行Y.Node.create。因为在Javascript中,字符串操作的速度比DOM操作快很多。在Y.Node.create调用前做的越多,代码执行的速度就越快。
所有widget还都会用到的方法就是bindUI。你可以在这个函数创建元素绑定事件,例如你可以给上面TEMPLATE中的&input&绑定valueChange事件,使textbox中值和配置属性中的对应的值始终保持一致。代码和用户都有可能去修改input中的值。如果是来自内部代码的更新值,textbox要刷新以显示新值,如果改变来自用户输入则不用刷新。如不这样,就会陷入死循环:textbox的value属性发生改变,触发change事件,响应函数又改变textbox的值,这一改变又触发了change事件……让我们看看如何处理这一情况。我们在input上绑定自定义的valueChange事件,当然要先在模块的requires中加入event-valuechange模块。
this._eventHandles.push(this._inputEl.after(‘valueChange’, this._afterInputChange, this));
我们假设_inputEl对象指向textbox。事件响应函数如下:
_afterInputChange: function (ev) {
this.set(VALUE, ev.target.get(VALUE),{source:UI});
这里假设我们事先已定义了表示value和ui的常量VALUE和UI。我们简单的将配置属性中的值设置为input中的值,我还给函数加了第三个参数{source:UI}。set函数可以接受第三个参数,这个参数是一个object对象,它的属性可以加入attribute change事件的事件对象(event facade)中,由此就可以区别textbox的valueChange事件是来自内部代码还是用户输入。在bindUI中,我们可能已经这样设置了事件监听:
this._eventHandles.push(this.after(‘valueChange’,this._afterValueChange));
这是一个监听value变化的事件监听,上一个例子也是对&input&的value的变化作监听。两个事件名称都一样,实际上,它们都是对一个叫做value的值的变化进行监听,但实际却不一样。通常,对属性变化的监听会放在initializer里,而此例涉及改变UI元素,所以把它放在bindUI中,也提醒我们这个事件响应涉及textbox。事件响应函数如下:
_afterValueChange: function (ev) {
if (ev.source === UI) {
this._inputEl.set(VALUE, ev.newVal);
首先我们检查事件对象的source属性。如果事件来自UI,我们直接忽略。在这里,属性名UI和它的值都是任意的,你可以根据自己喜好定义。我在设定value的属性值时定义了UI和它的值,所以在这里我就可以访问UI这一属性,你也可以用其它的键值对。实际上,widget也提供了一个相同功能的常数,只是名字有点长,所以我宁愿自己定义。
一个小技巧:你可以使用_set代替set来改变只读属性的值。_set方法本来是作为受保护方法,只能在类及其子类中访问的,但是javascript中对象成员都是公有的,所以_set实际上是个公共方法,外部也能访问。即使这样,我们还是会给只读属性声明readOnly:true,并且在API文档里也将这一属性标为只读。
最后一个实例方法是syncUI。前两个方法renderUI和bindUI会且仅会执行一次,但syncUI则至少被widget自身调用一次,你也可以在后面的程序中多次调用这个方法。syncUI的作用是根据对象的状态刷新其外观。对象的状态可能一直在变化, 界面也会跟着变化。不过,如何编写这个方法不能一概而论,要根据具体情况。对于简单的用户界面,syncUI可以在每次有变化发生时都重绘界面中的全部内容。而对于复杂的用户界面,重绘整个界面费时且会造成图像闪烁,所以你最好只重绘发生变化的部分,这样的话,你就需要将重绘不同部分的代码分别放在不同的函数中,syncUI会将每一部分只调用一次。还有,在先前的renderUI的例子中,我改变了textbox的值,而只有在syncUI执行之后,这一变化才能在屏幕上显示出来。
更常见的使用方法是给每个UI元素设置单独的重绘函数。这个函数会在初始化时被syncUI调用一次,之后会在配置属性的发生变化后,通过事件响应函数调用。例如
_valueUIRefresh: function (value) {
this._inputEl.set(VALUE, value);
这一函数和其它相似功能的重绘函数会在syncUI中被调用:
syncUI: function () {
this._valueUIRefresh(this.get(VALUE));
// other such refreshers
在after事件响应函数中的代码如下:
_afterValueChange: function (ev) {
if (ev.source === UI) {
this._valueUIRefresh(ev.newVal);
与其他模块的通信
在实现一个模块之后,它会和其他模块进行交互。传统的方法是紧耦合(通过的视频中我们可以了解什么是紧耦合什么是松耦合),也就是通过方法调用和属性赋值来将这些模块紧密联系在一起,在这儿就不赘述了。下面我们介绍另一种方法——自定义事件,里面包含了你所需要的全部方法。
首先,在initializer中,你要发布这个自定义事件,让大家都知道它:
initializer: function (cfg) {
this.publish(‘eventName’, { /*… options … */});
需要注意的是,事件名称最好是一个常见单词,因为在后面你会经常使用它,常见单词可以避免出现拼写错误。然后,假设你拥有一个对象,例如:
var myWidget = new Y.MyWidget({ /* .. attributes … */ });
此时,你可以为它绑定事件:
myWidget.after(‘eventName’, this._eventNameListener, this);
然而,这样做虽然不像直接的方法调用那样联系紧密,但是由于必须有一个的实例,所以其实质还是紧耦合。也就是说,在两个模块的通信中,一个模块必须知道另一个的细节,或者存在一个监视模块为它们建立连接。在这个过程中,有两个配置项是非常重要的,broadcast和emitFacade。
第一个,broadcast,可以让你在其他的模块中为这个事件设置监听器。broadcast默认值为0,此时只能用前面所示的那个方法。如果希望事件可以在任何地方被监听,你需要改变broadcast的值。如果只是在沙箱内,broadcast值为1,如果需要在各个沙箱间,则broadcast值为2。一个沙箱如下所示:
YUI().use( ‘module1′, …, ‘moduleN’, function (Y) {
// this is your sandbox
在页面中可以有多个这样的沙箱:
YUI().use( ‘module1′, …, ‘moduleN’, function (Y) {
// this is your sandbox
YUI().use( ‘moduleX-1′, …, ‘moduleX-N’, function (Z) {
// this is another sandbox
如果你设置broadcast值为2,你就可以在沙箱2中监听在沙箱1中发布的事件,具体细节请看。我们继续讨论简单沙箱的情况。
要在一个沙箱内监听另一个模块中发布的事件,必须知道的是这个模块的静态属性NAME的值和事件名称。回想下,Y.Base.create方法所带的第一个参数的值,就是NAME属性的值。因此,如果你创建了这样一个模块:
Y.MyWidget = Y.Base.create(
// … and so on
然后在发布了一个’事件:
initializer: function (config) {
this.publish(‘help’, {
broadcast: 1,
emitFacade: true
那么,要在其他模块的沙箱内监听这个事件,就可以这样做:
Y.after(‘xxxx:help’, function (ev) { … }, this);
在这里调用了Y.after,而不是myWidget.after,所以我不再需要一个实例才能触发这个事件。你也可以用同样的方法来监听DOM事件或者其他的自定义事件,比如’valueChange’等,不同的仅仅是引号前面的前缀。你也可以使用别的东西作为前缀,Y.base会接受这个值,但是通常情况下,Y.base提供的默认值已经足够了。你还需要设置emitFacade值,因为需要一个对象来触发事件,从而为ev.target提供门面值(facade value)。也许你会想,如果监听器所在的模块获得了注册事件模块的对象,那不是重新成为紧耦合了么。但事实并非如此,只要你在监听器模块中不保留这个对象,耦合就不复存在。此外,我们还有更好的办法。
在发布事件时,我们可以添加所有在事件对象中监听器所需要的信息,例如:
this.fire(‘help’, {helpTopic: ‘Event Broadcasting’});
方法的两个参数分别为发布事件的名称(也就是为它增加前缀的类的名称)和包含一些特性的对象(这些特性需要复制给事件对象)。这样监听器就不需要为了获取一些信息而遍历注册事件模块,从而达到了松耦合的目的。监听器通过“事件广播”知道有这样的一些模块,甚至可能有很多这样的模块会响应事件,但并不需要关注是哪一个模块正在响应它。这种方法也简化了日后新模块的添加。
事件和默认行为
要改变一个类的行为,通常的办法是建立一个子类,然后重写它的方法。也可以完成这些工作。你可以用来创建一个基类,然后用来创建一个新的类来作为基类的扩展,并给予其一些特殊的行为。例如,先创建基类:
Y.MySimpleWidget = Y.Base.create(
‘simpleWidget’,
// instance members here, amongst them:
renderUI: function () {
this.get(CBX).append(Y.Node.create(‘ … whatever goes into the widget … ‘ ));
// configuration attributes
// other static members
然后创建子类:
Y.MyFancyWidget = Y.Base.create(
‘fancyWidget’,
Y.MySimpleWidget,
renderUI: function () {
Y.MyFancyWidget.superclass.renderUI.apply(this, arguments);
this.get(CBX).append(Y.Node.create(‘ … add some bells and whistles … ‘ ));
// Presumably the fancy version does not need any further static members so I skip the last argument
我们可以看到,通过添加一些细节改进了。但是这在某些情况下会有些问题,所以你需要设计一个更灵活,更容易改变的基类。自定义事件会对你有所帮助。
假设有个排序类,拥有和两个参数,声明如下:
sort: function (key, direction) {
// sorting happens here
如果这个函数的行为在某些情况下需要更改,您可以这么做,在initializer方法中添加自定义事件:
initializer: function (config) {
// amongst many other things:
this.publish(SORT, {defaultFn: this._defSortFn});
若SORT是一个具有排序功能的实例,你可以这样声明它的sort函数:
sort: function(key, direction) {
this.fire(SORT, {key:key, direction:direction});
这样子,排序函数就转换为一个具有相同参数的事件触发函数。这样只是提供了一个转换接口,你仍然需要通过原始的排序函数来设计一个类:
_defSortFn: function (ev) {
var key = ev.key, direction = ev.
// same code as the original sort function
这个类的函数体与原始的方法一模一样,达到相同的排序目的。但是你可以从事件对象中知道和参数,只要一段简单的代码段就可以设置一个监听器,并且改变排序方法。
myObjectThatSorts.on(‘sort’, function (ev) {
var key = ev.key, direction = ev.
ev.preventDefault();
// now do your own sort
通过我让不再执行中的排序方法,从而可以根据我需要的结果做任何事,无论原来的排序是什么样。我甚至不用关心它是否停止,只要监听事件来翻转上用来显示排序方向的箭头。
我也可以改变事件对象。当事件触发时,我们得到的是根据事件对象复制的一个对象,它从监听器开始传播,通过默认的函数,到达监听器,然后被丢弃。你可以在过程中改变它的一些属性的值。当然,在默认方法执行后再做任何改变是没有影响的,在执行之前改变事件对象才能对方法有所影响。例如。
myObjectThatSorts.on(‘sort’, function (ev) {
ev.direction = (ev.direction===’desc’?'asc’:'desc’);
这样将得到倒置的排序结果。
在页面中调用一个模块的最简单的方法是通过标签来引用脚本,或者是将它放在标签的指向的文件中(可以通过手动连接或者支持的服务器自动完成)。而将自定义模块集成到中是一个更好的办法,可以极大的改进性能。这种方法很重要的一点是在使用插入模块时引入依赖文件(通过的最后一个参数),这样在调用时就可以按照正确的顺序调用它们。
对于小的应用,你可以在页面时加载所有内容,但是对于大型应用,这样是很不合理的,因为会花费太长的时间。你可以使用很多次方法去请求各个模块所需要的文件,然而这种让在模块加载时去查找本模块的依赖文件是非常耗时的,它可能需要建立很多个请求,直到获得它需要的文件为止。相反,你可以预先告诉各个模块和它的依赖文件,这样,当遇到这个模块时,加载器就可以并行的对它们进行处理。
为此,你需要为添加模块说明和要求来建立模块依赖关系,最简单的方法就是建立一个包含这些信息的文件(可以改为其他名字),如下所示:
YUI_config = {
filter:’raw’,
//combine:false,
gallery: ‘gallery--23-10′,
base: ‘build/’,
modules: {
‘myWidget’: {
path: ‘myWidget/myWidget.js’,
requires: ['widget', 'widget-parent', 'widget-child', 'widget-stdmod', 'transition'],
skinnable: true
// other modules here
// other groups here
将这段代码放在常规的标签内,并放在第一个代码段之前。它用来配置()库加载前需要的一些全局属性。,就像以前你必须放在的第一个参数一样,现在可以代替你做这些。你可以使用所列的任何配置项。
:“:产品代码(去除注释后的最简版本);
:版本(带有一些语句,包含组件);
:非最简版本(不带,含有注释);其中后两者通常只应用于开发环境中。
:这个配置项仅仅应用在后一些难解决的的查找时。
:如果你使用了模块,填上它的版本号。
:这个属性用来描述你的模块。
首先是组的名称,这里叫‘,也可以是其他名称。 你可以为放置在相同位置的一系列文件创建一个这样的组,每个组的第一个参数用来声明文件的根路径(可以是相对路径或者绝对路径)。除之外,还有一些组的基本属性,具体请参照。
最后是属性,需要在这列举这个组的所有模块。调用每个模块的关键词是模块的名称,也就是你在和的第一个参数。是模块相关文件的位置,可以是在基础上的相对路径,如果文件放在其他地方,也可以用全路径。其他的属性可以在找到,和放在结尾的一样。属性里面可以是或者你自己创建的,无论是这个组的还是其他组的。此外,如果设置的话,皮肤会被自动加载,就像我在文章开头所讲的那样。
为了简化这些工作,我创建了一个,可以为我自动配置。它会扫描并阅读每一个模块文件,并提取出每一个中的参数。对于我来说,它很有用,但并不一定适合你。
非常灵活,你可以通过很多方法建立你的模块。比如通过Y.Base来派生,其实我也不是经常这么做,,但在复杂系统中,依然非常推荐使用Y.Base。}

我要回帖

更多关于 杭州什么时候下雪 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信