javascript - jquery 求教:如何去思考一个JQ的组件封装?
大家讲道理
大家讲道理 2017-04-11 11:31:33
[JavaScript讨论组]

如何去思考一个JQ的组件封装?想请教一个比较详细的思路,我自身在思考的时候对于这个学习没有一个太理想的思路。主要是在JS的代码结构规划上面。

比如:我有一个表格,里面放进了数量,名字,高度,宽度,等等一系列的数据,数据格式object,array,number,fn()等等各种都有。
在外面调用这个组件时根据传入的数据将对应的信息显示出来。

这是一个别人封装的插件样例:


(function($ , undefined) {

    var Table = function (element, options) {
        this.$this = $(element);
        this.options = $.extend({}, $.fn.table.defaults, options);
        this.init();
    };
    // 使用proptotype给Table添加属性,
    Table.prototype = {
        constructor: Table,

        dataToObj: function(str){
            if(typeof str == "string" && str[0] == "&"){
                str = str.substring(1, str.length); 
            }
        
            if(typeof str == "string"){
                str = str.replace(/&/g,"','");
                str = str.replace(/=/g,"':'");
                str = "({'"+str +"'})";
                return eval(str); 
            }
        },

        init: function () {
            var options = this.options;
            if(!options.columns) return false;
            this.$this.html("");
            this.initTableHead();
            this.initTableBody(options.currentPage);
        },

        initTableHead: function(){
            var options = this.options,
                tableWrap = this.$this,
                attributesArr = [],
                theadStr = '';

            for(var i=0, len=options.columns.length; i< len; i++){
                theadStr += ''+options.columns[i].title+'';
                attributesArr.push(options.columns[i].dataIndex);
            }
            this.options.attributes = attributesArr;
            theadStr += '';
            this.$this.append(theadStr);
        },

        initTableBody: function(currentPage){
            var that = this, options = this.options, data = null;
                data = this.dataToObj(options.data);
            $.ajax({
                type: "POST",
                dataType: "json",
                url: options.url,
                data: options.data,
                beforeSend: function(){
                    Common.showLoading();
                },
                timeout: options.timeout,  
                complete: function(XMLHttpRequest, textStatus){
                    if(textStatus == "timeout"){ Common.dialogTip({info: "请求超时,请重试!!!"}); }
                    Common.hideLoading();
                },
                success : function(result){
                    if(result.errorCode == "106"){
                        location.href= result.info + "?goto=" + location.href; 
                    }else if(result.success) {
                        var totalCount = result.total,
                            attributes = options.attributes,
                            tbodyWrap = that.$this.find("tbody"),
                            rowStr = "",
                            tbodyStr = "";
                        if(result.data.length > 0){
                             $.each(result.data,function(index, row){
                                 var rowStr="";
                                 for(var key in attributes){
                                    var tddata=(row[attributes[key]] || "");
                                    if(row[attributes[key]] === 0){
                                        tddata = "0";
                                    }
                                    //去除html标签,如果有的话
                                    var reg =new RegExp("<\/?[^>]*>");
                                    if(reg.test(tddata)){
                                        tddata =tddata.replace(/<[^>]+>/g,"");
                                    }
                                     rowStr +=""+tddata+"";
                                 }
                                 rowStr+="";
                                 tbodyStr += rowStr;
                             });
                              tbodyWrap.html(tbodyStr);
                              that.rendererTd(result.data);
                              //加载分页
                              if(options.pagingBar && result.data.length > 0){
                                  pageNo = data ? data.pageNo : 0;
                                  that.loadPagingBar(result.total, parseInt(currentPage || pageNo));
                              }
                        }else{
                            tbodyWrap.html('没有找到任何数据!!!');
                            $('#'+options.pagingBarId).html("");
                        }
                        if(typeof(options.callback) == 'function'){
                            options.callback(result, data)
                        }
                    }else {
                        that.dialogTip(result.info);
                    }
                }
              });
        },

        rendererTd: function(rowsData){
            var columns = this.options.columns,
                rowElem = null,
                rendererTd = null;
            this.$this.children("tbody").children("tr").each(function(index, elem){
                rowElem = $(elem);
                $.each(columns, function(i, item){
                    if(columns[i].renderer && typeof columns[i].renderer == "function"){
                        rendererTd = rowElem.children("td:eq("+i+")");
                        columns[i].renderer.call(rendererTd, rendererTd.text(), rowsData[index]);
                    }
                });

            });
            var onRendered = this.options.onRendered;
            if (onRendered && typeof(onRendered) == "function") {
                onRendered.call(this);
            }
        },

        loadPagingBar: function(total, currentPage){
            total = total || 0;
            currentPage = currentPage || 1;
            var that = this,
                options = this.options,
                totalPages = Math.ceil(total/options.pageSize),
                pageWrap = $('#'+options.pagingBarId);
            //console.log(total + "......" +totalPages);
            pageWrap.html('');
            pageWrap.html('
    '); pageWrap.find('#pager-ext').twbsPagination({ totalPages:totalPages, visiblePages:options.visiblePages, startPage: currentPage, first: '第一页', prev: '上一页', next: '下一页', last: '最后页', onPageClick: function (event, page) { if(typeof options.onPageClick == "function"){ var obj = options.onPageClick(event, page); $.extend(that.options, obj); that.initTableBody(page); } } }); pageWrap.data("currPage", currentPage); pageWrap.find('#pager-ext').append('
  • 共'+totalPages+' 页
  • '); pageWrap.find('#pager-ext').append('
  • 总计 '+total+' 条
  • '); }, dialogTip: function(info){ var dialogStr = ''+ '

    '+ ''+ '

    '+ '

    '+ '

    '; var wrap = $(dialogStr); $("body").append(wrap); wrap.modal('show'); wrap.find(".close").on("click", function(){ wrap.modal('hide'); $("body").removeClass("modal-open"); wrap.remove(); }); } }; // option 可以是对象或者是方法名 // value option为方法名时的参数 $.fn.table = function (option, value) { var methodReturn; var $set = this.each(function () { var $this = $(this), options = typeof option === 'object' && option, data = new Table(this, options); if (typeof option === 'string') { methodReturn = data[option](value); }; }); return methodReturn ? $set : methodReturn; }; /* * columns = [{ title: '供货商来源', width: 150, dataIndex: 'c', renderer:function(value,obj){ * return value+':'+obj.d; * }}] * * */ $.fn.table.defaults = { url : "", data:"", pageSize:10, timeout: 10000, currentPage: 1, autoLoad:true, columns : null, pagingBar:true, hdBg:"success", pagingBarId: "paginationholder", visiblePages:7, onPageClick: null, onRendered: null, callback: null }; // 将插件类暴露给外界: $.fn.table.Constructor = Table; })(jQuery);
    大家讲道理
    大家讲道理

    光阴似箭催人老,日月如移越少年。

    全部回复(2)
    PHPz

    (一)思考该组件在项目中的文件结构

    通俗讲就是,你的组件代码及资源文件(可能包括模板html、css、js、img甚至依赖其他的子组件等等..)放在项目中的哪里?根据实际项目大小及业务场景的区分,一般都会将组件文件独立并解耦。

    (二)你的组件主要实现什么功能,如何渲染页面?

    题主描述的组件主要有2个功能:

    1. 根据调用时传入的数据进行数据过滤

    2. 将过滤后的数据通过表格的方式渲染在视图上

    (三)应用层如何调用你的组件?

    比如将你的组件挂载在 window 对象上? 或是模块化调用?

    (四)组件中涉及的数据结构是怎样的?

    如何存储 数量,名字,高度,宽度,等等一系列的数据 对应于 不同的数据格式?怎样的结构方便遍历查询?

    (五)思考实现组件的具体编码逻辑

    正如题主所说的通过 jQuery 操作DOM,暴露应用层的接口,创建组件实例并渲染页面,并且在编码过程中可以通过模拟接口来模拟组件实例的生成。


    ps:
    当然,一般的组件也会涉及 网络请求的处理,项目本身的一些静态配置及公有方法的调用,同其他组件间的调用联系 等等问题。如果你的组件层拥有很多组件的话,同时也可以为他们定义一个统一的执行生命周期(比如,组件属性设置-渲染-事件绑定-销毁),这样方便其他组件层开发人员的编码及后期维护。

    总之围绕着 Don't repeat yourself. 的编码原则的话,你会发现你的组件们会越来越健壮并且越来越可维护、可拓展。

    高洛峰

    jq插件大体上都是一样的。

    !(function($){//首先是一个立即执行函数,把jQuery传进来,性能更好
        var options = ... //一些使用选项
        function xxx(){...}  // 一些私有的函数    
        $.fn.plunge = function(settings){...}//把插件扔到prototype上暴露出去    
    })(jQuery);
    热门教程
    更多>
    最新下载
    更多>
    网站特效
    网站源码
    网站素材
    前端模板
    关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
    php中文网:公益在线php培训,帮助PHP学习者快速成长!
    关注服务号 技术交流群
    PHP中文网订阅号
    每天精选资源文章推送
    PHP中文网APP
    随时随地碎片化学习

    Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号