致我们终将组件化的 web -买球官网平台

2顶
0踩

致我们终将组件化的 web

2015-11-25 16:08 by 副主编 mengyidan1988 评论(5) 有16387人浏览
本文转自:
这篇文章将从两年前的一次技术争论开始。争论的聚焦就是下图的两个目录分层结构。我说按模块划分好,他说你傻逼啊,当然是按资源划分。





”按模块划分“目录结构,把当前模块下的所有逻辑和资源都放一起了,这对于多人独自开发和维护个人模块不是很好吗?当然了,那争论的结果是我乖乖地改回主流的”按资源划分“的目录结构。因为,没有做到js模块化和资源模块化,仅仅物理位置上的模块划分是没有意义的,只会增加构建的成本而已。
虽然他说得好有道理我无言以对,但是我心不甘,等待他日前端组件化成熟了,再来一战!
而今天就是我重申正义的日子!只是当年那个跟你撕逼的人不在。

模块化的不足
模块一般指能够独立拆分且通用的代码单元。由于javascript语言本身没有内置的模块机制(es6有了!!),我们一般会使用cmd或adm建立起模块机制。现在大部分稍微大型一点的项目,都会使用requirejs或者seajs来实现js的模块化。多人分工合作开发,其各自定义依赖和暴露接口,维护功能模块间独立性,对于项目的开发效率和项目后期扩展和维护,都是是有很大的帮助作用。
但,麻烦大家稍微略读一下下面的代码
require([
    'tmpl!../tmpl/list.html','lib/qqapi','module/position','module/refresh','module/page','module/net'
], function(listtmpl, qqapi, position, refresh, page, net){
    var foo = '',
        bar = [];
    qqapi.report();
    position.getlocaiton(function(data){
        //...
    });
    var init = function(){
        bind();
        net.get('/cgi-bin/xxx/xxx',function(data){
            rendera(data.banner);
            renderb(data.list);
        });
    };
    var processdata = function(){
    };
    var bind = function(){
    };
    var rendera = function(){
    };
    var renderb = function(data){
        listtmpl.render('#listcontent',processdata(data));
    };
    var refresh = function(){
        page.refresh();
    };
    // app start
    init();
});

上面是具体某个页面的主js,已经封装了像position,net,refresh等功能模块,但页面的主逻辑依旧是”面向过程“的代码结构。所谓面向过程,是指根据页面的渲染过程来编写代码结构。像:init -> getdata -> processdata -> bindevent -> report -> xxx 。 方法之间线性跳转,你大概也能感受这样代码弊端。随着页面逻辑越来越复杂,这条”过程线“也会越来越长,并且越来越绕。加之缺少规范约束,其他项目成员根据各自需要,在”过程线“加插各自逻辑,最终这个页面的逻辑变得难以维护。



开发需要小心翼翼,生怕影响“过程线”后面正常逻辑。并且每一次加插或修改都是bug泛滥,无不令产品相关人员个个提心吊胆。

页面结构模块化
基于上面的面向过程的问题,行业内也有不少买球软件推荐的解决方案,而我们团队也总结出一套成熟的买球软件推荐的解决方案:abstractjs,页面结构模块化。我们可以把我们的页面想象为一个乐高机器人,需要不同零件组装,如下图,假设页面划分为tabcontainer,listcontainer和imgscontainer三个模块。最终把这些模块add到最终的pagemodel里面,最终使用rock方法让页面启动起来。






下面是伪代码的实现
require([
    'tmpl!../tmpl/list.html','tmpl!../tmpl/imgs.html','lib/qqapi','module/refresh','module/page'
], function(listtmpl, imgstmpl, qqapi, refresh, page ){
 
    var tabcontainer = new rendermodel({
        rendercontainer: '#tabwrap',
        data: {},
        rendertmpl: "
  • {{item}}
  • ", event: function(){ // tab's event } }); var listcontainer = new scrollmodel({ scrollel: $.os.ios ? $('#page') : window, rendercontainer: '#listwrap', rendertmpl: listtmpl, cginame: '/cgi-bin/index-list?num=1', processdata: function(data) { //... }, event: function(){ // listelement's event }, error: function(data) { page.show('数据返回异常[' data.retcode ']'); } }); var imgscontainer = new rendermodel({ rendercontainer: '#imgswrap', rendertmpl: listtmpl, cginame: '/cgi-bin/getpics', processdata: function(data) { //... }, event: function(){ // imgselement's event }, complete: function(data) { qqapi.report(); } }); var page = new pagemodel(); page.add([tabcontainer,listcontainer,imgscontainer]); page.rock(); });

    我们把这些常用的请求cgi,处理数据,事件绑定,上报,容错处理等一系列逻辑方法,以页面块为单位封装成一个model模块。
    这样的一个抽象层model,我们可以清晰地看到该页面块,请求的cgi是什么,绑定了什么事件,做了什么上报,出错怎么处理。新增的代码就应该放置在相应的模块上相应的状态方法(preload,process,event,complete…),杜绝了以往的无规则乱增代码的行文。并且,根据不同业务逻辑封装不同类型的model,如列表滚动的scrollmodel,滑块功能的slidermodel等等,可以进行高度封装,集中优化。

    现在基于model的页面结构开发,已经带有一点”组件化“的味道。每个model都带有各自的数据,模板,逻辑。已经算是一个完整的功能单元。但距离真正的webcomponent还是有一段距离,至少满足不了我的”理想目录结构“。

    webcomponents 标准
    我们回顾一下使用一个datapicker的jquery的插件,所需要的步奏:
    1.引入插件js
    2. 引入插件所需的css(如果有)
    3. copy 组件的所需的html片段
    4. 添加代码触发组件启动
    现阶段的“组件”基本上只能达到是某个功能单元上的集合。他的资源都是松散地分散在三种资源文件中,而且组件作用域暴露在全局作用域下,缺乏内聚性很容易就会跟其他组件产生冲突,如最简单的css命名冲突。对于这种“组件”,还不如上面的页面结构模块化。
    于是w3c按耐不住了,制定一个webcomponents标准,为组件化的未来指引了明路。
    下面以较为简洁的方式介绍这份标准,力求大家能够快速了解实现组件化的内容。(对这部分了解的同学,可以跳过这一小节)

    1.