实现你人生中的第一个jQuery插件

前言

本想把本篇取名:从0开始写前端UI框架:实现你人生中的第一个jQuery插件 ,但感觉标题太长,所以简单明了直接取后面主题为题目吧。

前一篇文章 已经对my-ui框架做了简单的介绍。谈到了我是如何想起做这个框架的,并描绘了这个框架的大致功能或者说是组件,文章最后举了个已经实现的table表格功能为例子,万里长征就从这里开始!接下来咱们就来剖析如何实现这一简单的功能了,我已经实现了一个table组件功能,也叫做插件,那么接下来你就要开始来了解什么是插件,以及如何写一个插件了。

预备知识

科普插件知识:插件(Plug-in,又称addin、add-in、addon或add-on,又译外挂)是一种遵循一定规范的应用程序接口编写出来的程序。其只能运行在程序规定的系统平台下(可能同时支持多个平台),而不能脱离指定的平台单独运行。因为插件需要调用原纯净系统提供的函数库或者数据。

简而言之,插件就是现有框架的补丁或扩展,jQuery的插件必须要依赖于jQuery库。学习插件开发之前,你最好具备以下知识:

  1. JavaScript面向对象思想,把这种脚本语言当成面向对象来看待。重点是提升你的JavaScript封装能力。你可以尝试封装一个带有四则运算能力的计算器对象,或者封装一个可以自由切换选项卡的对象,或者一个抽奖功能等。这里的重点是练习你的封装能力,而不是算法。
  2. 理解JavaScript中this的功能,当this处于js文件裸体内,当this处于函数内,当this处于对象内分别指向的是谁。
  3. 理解JavaScript中的原型以及原型链,搞明白对象属性和原型属性有什么区别,并且弄明白prototype关键字的用意。
  4. JavaScript闭包和模块化编程,尤其是JavaScript为了避免变量污染如何做到隐藏对象作用域的。

嗯,上述几点我建议你在网上找找相关课程看看,或者翻阅专栏博客看看,当然了,实在找不到那翻阅我的CSDN博客看也行,不过我写的系列文章理解不深,只能作为入门级别的理解。

编写你人生的第一个jQuery插件

下面假设你已经具备上面我提到的前端编程技能了,那么接下来我们就来了解jQuery插件编程了。通过查阅jQuery帮助文档我们可以知道jQuery的插件是这样写的:

$.fn.jquery //.jquery 属性是通过 jQuery 原型赋值的,通过使用它的别名 $.fn 进行引用。

上诉语法将属性注册到jQuery对象原型上,这里多说一句的是:jQuery本身是一个对象,属于一个大的Ojbect。现在我想在这个大型的Object上面实现自己的方法,例子如下:

$.fn.extend({   
	"bold": function () {
        // 加粗字体
        return this.css({ fontWeight: "bold" });
    }
});

这时候jQuery对象就具备了一个叫做bold的属性,这个属性是一个函数。这时候jQuery的原有功能不变,额外多出了bold()函数,这个函数只实现一个功能:字体加粗。别着急运行上述代码,一般来说为了避免变量污染,我们会将上述代码放在一个匿名函数里面:

(function ($) {
            $.fn.extend({
                "bold": function () {
                    /// 加粗字体
                    return this.css({ fontWeight: "bold" });
                }
            });
        })(jQuery);

上述代码体现了JavaScript的闭包,首先定义了一个匿名函数,并且使用(jQuery)立刻调用它,并且传递进去jQuery对象,也就是那个$对象,匿名函数的内容我就不赘述了。接下来咱就可以调用这个咱基于jQuery扩展出来的方法了。

$(function() {
    $('#div').bold();
});

上述语法你应该很熟悉,就是jQuery的标准调用语法,只不过此刻jQuery多了个bold()函数功能摆了。

亲自试一试上述例子哦!

怎么样?你人生的第一个jQuery插件就这样开发出来了,这个功能虽然很简单,它利用jQuery原有的选择器语法选择出你想要的元素,然后再链式调用,将之前选中的元素传递给你的bold()函数。至于这个函数实现怎么样的内容,那是你说了算哦,这里想要注意的是你必须注意那个this指的是谁。

实现my-ui的table组件

既然你已经开发出了人生的第一款jQuery插件了,那么我们继续吧。

框架区别

有人说,框架分3种:

  1. 第一种是工具集toolkit。就是平时开发中经常用到的代码段封装成,比如处理字符串的代码段,比如处理日期的函数,众多工具集合在一起就是一个toolkit了。
  2. 第二种是Libaray。在Vue,React以及Angluar出来之前,我们也称它们为框架,比如jQuery,BootStrap,以前我们一直以为它是一个框架,但是现在你去看它的官网,官方给它的定义是库。
  3. 第三种是Framework,比较典型就是MVVM框架,人们认为这才是真正意义上的框架。

有图有真相,jQuery官网对jQuery的定义:在这里插入图片描述
Bootstrap对它自己的定义:
在这里插入图片描述

这一种好理解,就是工具集,关于第二种和第三种,有人总结区别如下:

库是将代码集合成的一个产品,供程序员调用。面向对象的代码组织形式而成的库也叫类库。面向过程的代码组织形式而成的库也叫函数库。

在函数库中的可直接使用的函数叫库函数。开发者在使用库的时候,只需要使用库的一部分类或函数,然后继续实现自己的功能。

框架则是为解决一个(一类)问题而开发的产品,框架用户一般只需要使用框架提供的类或函数,即可实现全部功能。可以说,框架是库的升级版。开发者在使用框架的时候,必须使用这个框架的全部代码。

框架和库的比较可以想像为:
假如我们要买一台电脑。框架为我们提供了已经装好的电脑,我们只要买回来就能用,但你必须把整个电脑买回来。这样用户自然轻松许多,但会导致
很多人用一样的电脑,或你想自定义某个部件将需要修改这个框架。而库就如自己组装的电脑。库为我们提供了很多部件,我们需要自己组装,如果某个部件
库未提供,我们也可以自己做。库的使用非常灵活,但没有框架方便。

api设计

上述已经讲述了框架之前的区别,我们要实现的框架属于是第二种:Libaray

我对my-ui的定位是:基于jQuery扩展实现的前端UI框架,封装统一API调用风格,汇聚众家优秀框架以及插件之所长,借鉴了easyui api设计风格,bootsrap的样式,追求视角完美的前端框架。

那么接下来第一步就是来设计api了,虽然我还未具备设计整个框架的能力,但是仿造easyui先设计table组件是没问题的。下面是我借鉴的easyui的api,如下:

table属性
属性名数据类型备注默认值
url字符串table表格请求后台的url地址null,必填
method字符串http请求方式,支持5种请求方式[get, post, put, delete, head]get,选填
函数名函数向组件种注册函数,一般在formatter属性中指定到该函数名,否则不会被调用null,选填
列属性
属性名数据类型备注默认值
field字符串从后台请求到的数据列表元素的属性名null,必填
formatter字符串数据格式化形式,框架提供了[number, date-box]类型,需要额外定义格式逻辑可指定回调函数名,调用回调函数时传递的参数列表分别是:value, row, indexnull,选填

开启你的编程之旅

看到这里,人生好像就有个小目标要去实现了,你要完成的工作是在jQuery对象上再添加原型对象和属性,来处理一个table表格,这个原型对象能接收url,medhod以及函数等参数,并且能去扫描页面中表格每一列的属性,生成一个table表格。接下来咱要编程实现这个功能。

假设抛开什么jQuery原型对象,什么抽象,封装,什么闭包,就是用jQuery来实现一个简单的表格,你是如何做的?我思考许久之后,我是这么做的。

1.定义table用到的css样式:

/**
* myui-table表格定义样式
*/
@charset "UTF-8";

caption {
	caption-side: top;
	text-align:left;
	padding: .5em;
	color: black;
	font-weight: bold;
	font-size: 15px;
}
.myui-table {
	width: 99%;
	border-collapse: collapse;
	margin: 3px;
	font-family: "Trebuchet MS", Arial, Helvetica, sans-serif;
}

.myui-table td, .myui-table th {
	font-size: 1em;
	border: 2px solid rgba(255, 255, 255, 1);
	padding: 3px 3px 2px 3px;
	text-align: center;
}

.myui-table th {
	height: 45px;
	font-size: 1.1em;
	padding-top: 5px;
	padding-bottom: 4px;
	background-color: #4fa6db;
	color: #ffffff;
}

.myui-table tbody tr:nth-child(even) td {
	background-color:#f9f9f9;
	height: 28px;
}

.myui-table tbody tr:nth-child(odd) td {
	background-color:#eeeeee;
	height: 28px;
}

2.在html页面中定义table元素

<table id="table" class="myui-table">
            <caption>梁山好汉排名</caption>
            <tbody><tr>
                <th style="height: 5px;" field="index">座次</th>
                <th style="height: 5px;" field="name">原名</th>
                <th style="height: 5px;" field="nickName">昵称</th>
                <th style="height: 5px;" field="constellation">星宿</th>
                <th style="height: 5px;" field="birthDay" formatter="daily-date">生日</th>
                <th style="height: 5px;" field="sex" formatter="sexFormatter">性别</th>
                <th style="height: 5px;" field="effectiveness" formatter="number">战斗指数</th>
                <th style="height: 5px;" field="specialSkills">特殊技能</th>
                <th style="height: 5px;" field="remark">备注</th>
            </tr>
        <tr><td>1</td><td>宋江</td><td>呼保义</td><td>天魁星</td><td>1995-01-01</td><td></td><td>4.12</td><td>忠义双全</td><td>梁山好汉大BOOS</td></tr><tr><td>2</td><td>卢俊义</td><td>玉麒麟</td><td>天罡星</td><td>1996-02-01</td><td></td><td>4.32</td><td>替天行道</td><td>梁山No.2人物</td></tr><tr><td>3</td><td>吴用</td><td>星智多星</td><td>天机星</td><td>2000-01-01</td><td></td><td>5.00</td><td>锦囊妙计</td><td>我军及友军精神力上升</td></tr><tr><td>4</td><td>公孙胜</td><td>入云龙</td><td>天闲星</td><td>2001-01-01</td><td></td><td>4.31</td><td>五雷天罡正版法</td><td>区域内敌人禁咒</td></tr><tr><td>5</td><td>孙二娘</td><td>母夜叉</td><td>地壮星</td><td>1999-01-01</td><td></td><td>2.56</td><td>未知</td><td>地壮星母夜叉孙二娘</td></tr><tr><td>6</td><td>景住</td><td>金毛犬</td><td>地狗星</td><td>1996-01-01</td><td></td><td>2.12</td><td>判官笔</td><td>梁山好汉第108位好汉</td></tr></tbody></table>

这样子你的页面展示如下:
在这里插入图片描述

函数级别的封装

上面已经页面效果渲染出来了,接下来你应该很快想到最简单级别,函数级别的封装了。你要将上诉代码封装在一个JavaScript函数里面,table body元素是动态生成出来的。那么接下来你的代码就变成这样子了:

html代码

        <table id="table" class="myui-table">
            <caption>梁山好汉排名</caption>
            <tr>
                <th style="height: 5px;" >座次</th>
                <th style="height: 5px;" >原名</th>
                <th style="height: 5px;" >昵称</th>
                <th style="height: 5px;" >星宿</th>
                <th style="height: 5px;" >生日</th>
                <th style="height: 5px;" >性别</th>
                <th style="height: 5px;" >战斗指数</th>
                <th style="height: 5px;" >特殊技能</th>
                <th style="height: 5px;" >备注</th>
            </tr>
        </table>

这时候你需要写一个JavaScript代码段处理这个表格如下:

            $.ajax({
                type: 'get',
                dataType: "json",
                url: 'xxx/xxx/xxx'
                success: function (data) {
                    var table = $("#table");
                    if (data.length > 0) {
                        var html = "";
                        for (var i = 0; i < data.length; i++) {
                            html += "<tr>" +
                                "<td>" + data[i].字段名1 + "</td>" +
                                "<td>" + data[i].字段名2 + "</td>" +
                                "<td>" + data[i].字段名3+ "</td>" +
                                "<td>" + data[i].字段名4 + "</td>" +
                                ......
                                "<td>" + data[i].字段名9 + "</td>" +
                                "</tr>";
                        }
                        $(html).appendTo(table);
                    }
                }
            });

如果你是有jQuery编程经验的开发者,你应该看到,这个代码段只做了两件事:

  1. ajax请求后台数据
  2. 拿到请求的数据循环遍历填充table表格

插件级别的封装

如果你感觉上诉代码没问题了,并且我相信你很轻松的看出我是怎么实现的,并且你实现的会更好。那么咱可以继续下去了,接下来就是插件级别的封装。

回顾本篇开头第2小节提到的jQuery插件开发,咱得把这段代码逻辑注册到jQuery对象原型上去。封装的代码如下:

(function($) {

	//定义全局变量
	var url = "";

	var method = "GET";

	// plugin definition
	$.fn.table = function(options) {
		url = options.url ||'';
		method = options.method || "GET";

		// 
		$.ajax({
			url: url,
			method: method,
			success:function (response, index) {
               //上诉ajax响应体里面的代码
			}
		});
	};
	//  ...
})(jQuery);

此时调用该插件的代码改为

 $('#table').table({
     url : './table_data.json',
 });

如果你已经具JavaScript对象知识,不难看到,我的table组件传过去一个字面量Object对象,对象就一个属性:url,属性值.是一个json串的url地址。而插件的构造函数中有一个options变量,可以接收Object对象,这样的好处就是参数传递很容易扩展。

api完整实现

Ok,到此可以完全来实现设计的api需求了,那个api设计中大概要求我们:

  1. 能动态接收请求数据地址url参数,必选
  2. http请求方式,可选,默认值为get
  3. 请求到的数据,可以每一列filed属性属性值填充数据
  4. 数据可能需要格式化,比如返回一个金额123.456,用户可能只需要展示12.34。比如返回性别可能是true/alse,但是展示的时候是男/女等

完整的代码如下

(function($) {

    // plugin definition
    $.fn.table = function(options) {

        //放弃全局变量定义的用法,避免一个页面引用两次插件数据冲突

        this.url = options.url ||'';
        this.method = options.method || "GET";
        this.dataOptions = [];
        this.datas = [];

        this.options = options || {};

        var _this = this;

        //遍历thread的所有tr,只取带有field的列
        var theads = $(this).find("tr");
        $(theads).each(function(index, trObject) {
            $(trObject).children().each(function(index, tdObject) {
                if(typeof($(tdObject).attr("field")) != "undefined") {
                    var ojbect = {};
                    ojbect.field = $(tdObject).attr("field");
                    ojbect.formatter = $(tdObject).attr("formatter");
                    _this.dataOptions.push(ojbect);
                }
            });
        });

        //请求数据
        $.ajax({
            url : this.url,
            method : this.method,
            success:function (response, index) {
                $.each(response, function(index, value) {
                    _this.datas.push(value);
                });

                var table = _this[0];

                var html = getHtml(_this.datas, _this.dataOptions, _this.options);
                $(html).appendTo(table);
            }
        });
    };

    // 计算table的html元素
    function getHtml(datas, dataOptions, options) {
        var html = "";

        //双重for循环变量属性和值
        $(datas).each(function(index, value){
            html += "<tr>";
            $(dataOptions).each(function (index1, value1) {

                if(value1.field === 'index') { //处理序号
                    html += "<td>" + formatter(index+1, null) +"</td>";
                } else if(options.hasOwnProperty(value1.formatter) && typeof options[value1.formatter] === 'function') { //处理回调函数
                    //调用回调函数,并返回值
                    var result = options[value1.formatter].call(this, value[value1.field], value, index);
                    html += "<td>" + result +"</td>";
                } else { //处理其他类型
                    html += "<td>" + formatter(value[value1.field], value1.formatter) +"</td>";
                }
            });
            html += "</tr>";
        });

        return html;
    };

    //格式化数据
    function formatter(value, formt) {

        if(undefined != formt && value != null) {
            switch(formt) {
                case 'number':
                    value = value.toFixed(2);
                    break;
                case 'daily-date':
                    '2020-01-01'
                    break;
                default:
                    value = value;
            }
        }
        return value;
    }

    //  ...
})(jQuery);

上诉代码已经完全实现了api的基本功能,此可你可以把代码段放到单独的js文件中去了,此时调用插件的代码变为:

$('#table').table({
        url : './table_data.json',
        sexFormatter : function (value, row, index) { //性别格式化回调函数
            if(value) {
                return "男";
            } else if(!value) {
                return "女";
            }
            return value;
        }
    });

到此,我人生的第一款jQuery插件已经开发出来了,并且已经托管到github上面去了,如果你在动手试验过程中有问题或者想运行现成的案例,可以直接访问我的项目地址download源码来直接运行观看效果哦!

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 护眼 设计师:闪电赇 返回首页