如何写一个前端组件-以bootstrap-tab为例

介绍

本文以Bootstrap标签页组件为例,介绍如何编写或者封装一个前端组件,以下是实现效果:

Bootstrap-tab

原生的Bootstrap-tab组件主要有html,css组成,开发者使用时,需要写很多代码,不易于使用,对bootstrap-tab封装后,可以更方便地使用,同时提供关闭、增加tab页、指定当前选中页、即使加载等功能,这样组件可以适配更多的场景。

原生bootstrap-tab组件使用可参考https://www.runoob.com/bootstrap/bootstrap-tab-plugin.html

其中官网一段实例代码是:


<ul id="myTab" class="nav nav-tabs">
    <li class="active"><a href="#home" data-toggle="tab">
            菜鸟教程</a>
    </li>
    <li><a href="#ios" data-toggle="tab">iOS</a></li>
    <li class="dropdown">
        <a href="#" id="myTabDrop1" class="dropdown-toggle"
           data-toggle="dropdown">Java <b class="caret"></b>
        </a>
        <ul class="dropdown-menu" role="menu" aria-labelledby="myTabDrop1">
            <li><a href="#jmeter" tabindex="-1" data-toggle="tab">
                    jmeter</a>
            </li>
            <li><a href="#ejb" tabindex="-1" data-toggle="tab">
                    ejb</a>
            </li>
        </ul>
    </li>
</ul>
<div id="myTabContent" class="tab-content">
    <div class="tab-pane fade in active" id="home">
        <p>菜鸟教程是一个提供最新的web技术站点,本站免费提供了建站相关的技术文档,帮助广大web技术爱好者快速入门并建立自己的网站。菜鸟先飞早入行——学的不仅是技术,更是梦想。</p>
    </div>
    <div class="tab-pane fade" id="ios">
        <p>iOS 是一个由苹果公司开发和发布的手机操作系统。最初是于 2007 年首次发布 iPhone、iPod Touch 和 Apple
            TV。iOS 派生自 OS X,它们共享 Darwin 基础。OS X 操作系统是用在苹果电脑上,iOS 是苹果的移动版本。</p>
    </div>
    <div class="tab-pane fade" id="jmeter">
        <p>jMeter 是一款开源的测试软件。它是 100% 纯 Java 应用程序,用于负载和性能测试。</p>
    </div>
    <div class="tab-pane fade" id="ejb">
        <p>Enterprise Java Beans(EJB)是一个创建高度可扩展性和强大企业级应用程序的开发架构,部署在兼容应用程序服务器(比如 JBOSS、Web Logic 等)的 J2EE 上。
        </p>
    </div>
</div>
<script>
    $(function () {
        $('#myTab li:eq(1) a').tab('show');
    });
</script>

那么如何封装或者开发一个组件呢?

组件开发步骤

Step1:结构化静态代码,梳理核心的问题

在组件开发流程中,可能拿到前端设计的静态代码(html+css的组合),这时候要拆解代码结构,使得结构能够模板化。其次梳理核心问题,bootstrap-tab组件化之后,应该能够动态加载tab内容,这个可以通过jquery.load方法解决,这样可以做到主页面和子页面解耦。

读懂了静态代码,理解了结构和核心问题就可以写代码了,首先搭建组件的架子。

Step2:组件骨架

/**
 * Bootstrap tab组件封装
 * @author billjiang  qq:475572229
 * @created 2017/7/24
 *
 */
(function ($, window, document, undefined) {
    'use strict';

    var pluginName = 'tabs';

    //入口方法
    $.fn[pluginName] = function (options) {
        var self = $(this);
        if (this == null)
            return null;
        var data = this.data(pluginName);
        if (!data) {
            data = new BaseTab(this, options);
            self.data(pluginName, data);
        }
        return data;
    };


    var BaseTab = function (element, options) {
        this.$element = $(element);
        this.options = $.extend(true, {}, this.default, options);
        this.init();
    }

    //默认配置
    BaseTab.prototype.default = {
   }

    //结构模板
    BaseTab.prototype.template = {
    }

    //初始化
    BaseTab.prototype.init = function () {
    }  
})(jQuery, window, document)

搭建了以上组件的骨架,并对组件命名为tabs,这样就可以通过$("#tab-container").data("tabs")获取组价的方法和属性。在入口方法中,会将初始化后的对象缓存到页面html中,这样可以避免重复创建对象。一些经典的开源前端组件都是这样写法,比如Bootstrap-treeview,大家有时间可以看看它的源码。

以上的写法使用原型链的写法。定义了默认配置,结构模板,初始化入口。

编写代码

在组件的代码骨架里,填充模板代码,这里使用占位符{0},{1}等表示外部传入的变量,然后在init方法中校验外部传入数据的合法性,然后构建组件,并且绑定关闭事件、点击事件。

在开发前端组件的时候,往往不知道默认参数应该有什么,可以在开发的时候,用到就加上去,这里加了两个默认参数,一个showIndex是默认显示的tab页索引,一个loadAlltab是否一次性把所有的页面数据加载完。

具体的逻辑请看下面的代码:

  //默认配置
    BaseTab.prototype.default = {
        showIndex: 0, //默认显示页索引
        loadAll: true,//true=一次全部加在页面,false=只加在showIndex指定的页面,其他点击时加载,提高响应速度

    }

    //结构模板
    BaseTab.prototype.template = {
        ul_nav: '<ul  class="nav nav-tabs"></ul>',
        ul_li: '<li><a href="#{0}" data-toggle="tab"><span>{1}</span></a></li>',
        ul_li_close: '<i class="fa fa-remove closeable" title="关闭"></i>',
        div_content: '<div  class="tab-content"></div>',
        div_content_panel: '<div class="tab-pane fade" id="{0}"></div>'
    }

    //初始化
    BaseTab.prototype.init = function () {
        if (!this.options.data || this.options.data.length == 0) {
            console.error("请指定tab页数据");
            return;
        }
        //当前显示的显示的页面是否超出索引
        if (this.options.showIndex < 0 || this.options.showIndex > this.options.data.length - 1) {
            console.error("showIndex超出了范围");
            //指定为默认值
            this.options.showIndex = this.default.showIndex;
        }
        //清除原来的tab页
        this.$element.html("");
        this.builder(this.options.data);
    }

    //使用模板搭建页面结构
    BaseTab.prototype.builder = function (data) {
        var ul_nav = $(this.template.ul_nav);
        var div_content = $(this.template.div_content);

        for (var i = 0; i < data.length; i++) {
            //nav-tab
            var ul_li = $(this.template.ul_li.format(data[i].id, data[i].text));
            //如果可关闭,插入关闭图标,并绑定关闭事件
            if (data[i].closeable) {
                var ul_li_close = $(this.template.ul_li_close);

                ul_li.find("a").append(ul_li_close);
                ul_li.find("a").append("&nbsp;");
            }

            ul_nav.append(ul_li);

            //div-content
            var div_content_panel = $(this.template.div_content_panel.format(data[i].id));


            div_content.append(div_content_panel);
        }

        this.$element.append(ul_nav);
        this.$element.append(div_content);
        this.loadData();

        this.$element.find(".nav-tabs li:eq(" + this.options.showIndex + ") a").tab("show");

    }

    BaseTab.prototype.loadData = function () {
        var self = this;

        //tab点击即加载事件
        //设置一个值,记录每个tab页是否加载过
        this.stateObj = {};
        var data = this.options.data;
        //如果是当前页或者配置了一次性全部加载,否则点击tab页时加载
        for (var i = 0; i < data.length; i++) {
            if (this.options.loadAll || this.options.showIndex == i) {
                if (data[i].url) {
                    $("#" + data[i].id).load(data[i].url);
                    this.stateObj[data[i].id] = true;
                } else {
                    console.error("id=" + data[i].id + "的tab页未指定url");
                    this.stateObj[data[i].id] = false;
                }
            } else {
                this.stateObj[data[i].id] = false;
                (function (id, url) {
                    self.$element.find(".nav-tabs a[href='#" + id + "']").on('show.bs.tab', function () {
                        if (!self.stateObj[id]) {
                            $("#" + id).load(url);
                            self.stateObj[id] = true;
                        }
                    });
                }(data[i].id, data[i].url))
            }
        }

        //关闭tab事件
        this.$element.find(".nav-tabs li a i.closeable").each(function (index, item) {
            $(item).click(function () {
                var href = $(this).parents("a").attr("href").substr(1);
                $(this).parents("li").remove();
                $("#" + href).parent().remove();
            })
        });

    }

测试

编写一个前端界面,测试组件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Tab组件</title>
</head>
<link rel="stylesheet" href="bootstrap/css/bootstrap.min.css">
<link rel="stylesheet" href="font-awesome/css/font-awesome.min.css">
<link rel="stylesheet" href="../css/bootstrap-tab.css">

<body>
<div id="tabContainer"></div>
</body>
<script src="jquery/jquery-1.8.3.min.js"></script>
<script src="bootstrap/js/bootstrap.min.js"></script>
<script src="../js/bootstrap-tab.js"></script>

<script>
    $("#tabContainer").tabs({
        data: [{
            id: 'home',
            text: '百度一下',
            url: "tab_first.html",
            closeable:true
        }, {
            id: 'admineap',
            text: 'AdminEAP',
            url: "tab_second.html"
        }, {
            id: 'edit',
            text: '编辑人员',
            url: "tab_content.html",
            closeable:true
        }],
        showIndex:1,
        loadAll:false
    })
</script>
</html>

通过配置各种参数,看看组件是否满足了预期的要求。

组件化后的Bootstrap-tab

扩展

组件在使用的过程中还会遇到各种问题,或者各种需求,比如新增一个tab页面,比如获取当前tab的ID或index,这是可以在代码中按需扩展。


    //新增一个tab页
    BaseTab.prototype.addTab=function (obj) {
        //nav-tab
        var ul_li = $(this.template.ul_li.format(obj.id, obj.text));
        //如果可关闭,插入关闭图标,并绑定关闭事件
        if (obj.closeable) {
            var ul_li_close = $(this.template.ul_li_close);

            ul_li.find("a").append(ul_li_close);
            ul_li.find("a").append("&nbsp;");
        }

        this.$element.find(".nav-tabs").append(ul_li);
        //div-content
        var div_content_panel = $(this.template.div_content_panel.format(obj.id));
        this.$element.find(".tab-content").append(div_content_panel);
        $("#" + obj.id).load(obj.url);
        this.stateObj[obj.id] = true;

        if(obj.closeable){
            this.$element.find(".nav-tabs li a[href='#" + obj.id + "'] i.closeable").click(function () {
                var href = $(this).parents("a").attr("href").substr(1);
                $(this).parents("li").remove();
                $("#" + href).parent().remove();
            })
        }

        this.$element.find(".nav-tabs a[href='#" + obj.id + "']").tab("show");
    }


    //根据id设置活动tab页
    BaseTab.prototype.showTab=function (tabId) {
        this.$element.find(".nav-tabs li a[href='#" + tabId + "']").tab("show");
    }

    //获取当前活动tab页的ID
    BaseTab.prototype.getCurrentTabId=function () {
        var href=this.$element.find(".nav-tabs li.active a").attr("href");
        href=href.substring(1);
        return href;
    }

更完善的bootrap-tab版本已经开源,详见我的Github地址:

bootstrap-tabhttps://github.com/bill1012/bootstrap-tab

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值