我是FE,也是Fe

前端来源于不断的点滴积累。我一直在努力。

统计

留言簿(15)

阅读排行榜

评论排行榜

谈谈前端组件库

读过,了解过很多的前端控件库/组件库,尝试过,体验过多个失败的,不算失败的组件库之后,总结下来,觉得要构建一个完整的组件库,需要考虑以下几个方面的问题:

1.基础库:

注意是库,不是框架,基础库通常提供底层方法,它必须能够屏蔽浏览器/终端的API差异。也许大家脑子里面会弹出一堆前端热门的一些库。在此不讨论哪个库哪个库怎么样,一个基础库必须提供的功能:
  • 基本类型的常见扩展:原生的javascript对象API往往在现实中不够用,比如常见的Array.indexOf/remove/each,Date.parse/format,不管是怎么封装都需要这类方法
  • DOM操作常见方法:DOM节点增删改查,CSS selector,DOMReady,contains,add/remove/toggleClass,屏蔽浏览器之间的操作差异。不多说,人人都熟。
  • 一套浏览器检测机制:以前大家都倾向于做浏览器类型和版本的检测,现在倾向于做浏览器的特性检测,这样更有实际用处。
  • Ajax的封装,对于组件库来讲可无。毕竟组件库本身的实现不太会用得上Ajax。

2.事件机制:

addEventListener/removeEventListener/dispatchEvent 是常见的封装方式,不应该只是DOM事件,而是任何对象都可以做一个事件机制。
对于DOM事件的封装需要屏蔽IE/标准Event的差异提供为用户使用,事件代理非常重要,不可小视。
  • 事件机制都无一例外的是基于第三方观察者或者叫做沙箱(Sandbox)实现。
  • 事件机制更深的功能是提供一个模块的通信机制。
  • 对于组件库,组件实例之间的通信更加重要,组件实例之间最好不要存在相互引用的关系,互相不能感知对方的存在,有了事件机制,可以通过第三方有效的通知组件实例,减少组件实例间的耦合。

3.模板机制:

实际上写组件的时候,拼装html是一件很复杂的事情,模块能够从数据模型,对于组件库来讲通常是配置信息选项。将这些选项拼装成html字符串。但是大家普遍的一个误区是在追求语法的简便的和性能。我倒觉得模板要做的事情远不止如此。功能强大的模板不仅仅只完成字符串的拼接,而是要简化整个DOM操作,从数据模型到DOM的双向绑定,Model更新了DOM也随之更新。甚至要解决动态DOM事件绑定的问题。


4.面向对象的机制

放在组件库这个角度去谈面向对象的时候,他是一个架构设计中的一个重要的一环。
面向对象的机制能有效的提升代码的可复用性和扩展性,javascript灵活的语法诸如prototype/closure的方式,能构建出一个强大的类库。
可以使用继承机制扩展已有的组件。也可以用引用的方式装饰(Facade)现有组件,个人更倾向于使用装饰。因为继承总会不可避免的直接或者间接去访问父类的一些私有属性方法。
这个机制其实决定了一个组件的代码模型,通常需要解决的问题有:
  • 该组件继承自哪些组件或者基础类,或者依赖于那些类?
  • 组件实例的管理方式,因为每个组件实例都需要在一个容器中统一存放,理想的的存放模型应该是树形的,在内存中存在类似DOM树一样的组件对象树,是否可以通过类名找到相关实例,根据ID获取实例,获取子实例,父实例,父/子实例之间的通信父实例的resize是否能通知容器内的实例resize。
  • 插件机制:作为一个非常重要的扩展机制,插件能有效的解决组件间的复用部分,通常这部分会叫做行为(behavior),对于组件不能提供的甚至是个性化的功能,有没有提供有效的,足够多的扩展点。
  • 提供怎样的实例化方式? new XXX() ?? 还是类似DOM的操作方式appendInstance??甚至有类似jq这种链式。我更觉得应该使用appendInstance的方式,这样能更加有效的体现组件示例间的父/子关系。就像DOM操作一样,最终组件实例也是树形结构,如果我们直接new XXX() 这种方式,其实相当于声明了一个游离态的 DOM节点。实际我写代码的时候发现要管理这些组件实例也是比较麻烦的地方,试想一个页面如果有多个组件实例,需要声明多少个实例变量,或者申明多少个对象去存放这些实例。
  • 组件提供的API,一个组件对外暴露的API会包括初始构造方法,公共方法(method),事件(event),对于event,提供怎样的eventData也非常重要。

5.模块化机制

如今模块化的思想已经深入人心,模块化带来了很好的团队多人完成一项大的任务的可能性,符合高内聚低耦合的思想。到了如今这个时代,万物皆模块。
组件库通常是一个庞大的工程,单靠个人英雄主义很难做的完整全面。
详细的来讲,模块化机制涉及:

  • 模块本身的定义,注册,直接影响一个组件的代码模型,一个组件是一个模块。
  • 模块的依赖申明以及追朔机制:就像前面提到的,依赖于那些类,css文件,资源,数据。不仅仅需要声明,还应该可追朔,依赖的父类,也能找到父类本身所依赖的资源,这样为按需部署打包,在线调试提供居多方便。
  • 加载机制:因为在开发阶段要么放一个整个组件库代码,要么是通过一个加载器按需加载,到了线上希望只部署引用到了模块组件,这样可以减少实际部署的文件大小。加载机制会涉及到浏览器的javascript/css文件的加载,尤其是需要尽可能的并行下载而且按照依赖关系先后执行。包括应用模块,可以方便的通过这种加载机制延迟,按需,按时的加载到页面中。
  • 打包部署机制:由于依赖可追朔,这样实际项目中用到的那些组件可以分析出来,最终可以根据实际使用到的情况打包出适合大小的组件库,减小冗余包的存在。
  • 模块间的通信机制,由于模块减轻耦合甚至是独自孤立存在,组件之间的通信就非常重要,比如通常一个页面上面的菜单组件实例点击需要触发下面组件的更新。如果直接监听菜单事件去更新下面的组件,也许菜单是每个页面都有。但是下面的组件不是每个页面都有,这样的事件监听就显然耦合较重,互相依赖对方的存在。如果菜单点击这是告诉第三方我被点击了。下面的组件只去监听第三方的事件,这样的代码思路明显要好过很多。
需要再次强调的是万物皆模块,这意