原生JavaScript 仿 echart 插件制作自己的插件库

本文分享了一种自定义Table插件的制作思路,通过封装HTML的table标签,实现在HTML中几乎无需编写代码即可生成可复用的报表,极大地提高了开发效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文系原创,转载请注明出处:

https://blog.youkuaiyun.com/chengbao315/article/details/84171511

最近在做一个数据大屏的web项目,页面中经常会用到一些报表、图表之类的功能,对于图表我选择了 echart 插件,因为 echart 做图表非常方便,效果也比较满足要求,但是对于图表,我则用的是 html 的 table 标签,每次做都要敲一大堆 html 代码,还要重复的编写和修改 css 样式。都面向对象编程这么多年了,这种效率的开发方式怎么能符合我的气质?!!何况同样是做图表的 echart 的怎么使用那么方便? 于是我决定自己写一个插件库。

好了,进入主题,今天我要跟大家分享一个table 插件的制作思路,今后大家可以根据这个思路完善自己的插件库,当然如果我做好了,也会跟大家分享。话不多说,先看效果:

这是一个非常简单的 table 报表,但是如果你想实现这样的效果的话,我相信你也需要敲不少代码。而我的设计却可以在 html 里几乎不用写任何代码,并可以在其他场景无限复用。不信你看,我的 html 代码如下:

<head>
    <title>Document</title>
    <script src="cb_table.js"></script>
</head>

<body>
    <div id="mytable" style="width:300px; height:200px; background: black"></div>
    <script>
      var mytable = cb_tables.init('mytable');
      var option = {
        title: {
            text: '员工信息表',
        },
        dataSet: {
            data: [['姓名', '性别', '年龄', '部门']],
            default: {
                columns: 4,
                rows: 6,
            }
        },
      };
      mytable.setOption(option);
    </script>
</body>

可以看到,我这里用于显示报表的是一个 div 块,那么首先要做的是引入我设计的 cb_table.js 插件,然后初始化对象传入 div 标签的 id 属性,最后根据需要设置自己喜欢的 option 属性就可以实现上述的报表功能。怎么样?用过 echart 的伙伴是不是对于这种操作似曾相识啊?接下来看我怎么实现的吧:

首先,我们新建一个 .js 文件,插件叫什么名大家自己定吧,然后我需要定义一个对象,里面定义一个 init() 方法,在 init() 方法中我们可以创建一个 table 对象,并进行简单的样式设置,代码如下:

var cb_tables = {
    init: function (id) {
        var div_tab = document.getElementById(id);
        var table = document.createElement("table");
        div_tab.appendChild(table);
        table.style.width = '100%';
        table.style.height = '100%';
        table.style.borderCollapse = 'collapse';
        return new Mytable(table);
    },
}

完成这步,就实现了将你的 div 中添加了一个 table 标签,并且返回一个 Mytable 类型的对象。为什么要这么写,因为这是一种很好的封装设计,至始至终用户都不知道我生成的 table 到底长什么样的,拥有哪些属性和样式,你能做的只能是按照我提供的接口调用我的方法。哈哈哈,私有化属性就是这么牛。

接下来看我的 Mytable 构造方法如何写的吧,这里需要强调一下,构造函数和普通的函数没有什么区别,但是构造函数首字母一定要大写!

function Mytable(table) {
  this.table = table;
    this.setOption = function (option) {
        // 复制全部设置属性
        option = copyOption(options, option);
        this.table.style.border = option.table.border;
        if(!!option.title.text){
            var th = document.createElement("caption");
            table.appendChild(th);
            th.innerHTML = option.title.text;
            th.style.color = option.title.textStyle.color ;
            th.style.textAlign = option.title.x ;
            th.style.width = '100%';
            th.style.height = '15%';
            th.style.fontFamily = option.title.textStyle.fontFamily;
            th.style.fontSize = option.title.textStyle.fontSize;
            table.style.height = '87%';
        }

        var columns = option.dataSet.default.columns,
            rows = option.dataSet.default.rows;
        for (var i = 0; i < rows; i++) {
            var tr = document.createElement("tr");
            table.appendChild(tr);
            tr.style.height = 100 / rows + '%';
            tr.style.backgroundColor = i % 2 ? option.table.dColor : option.table.sColor;
            for (var j = 0; j < columns; j++) {
                var td = document.createElement("td");
                tr.appendChild(td);
                td.style.width = 100 / columns + '%';
                td.style.border = option.table.border;
                td.style.color = i == 0 ? option.table.header.textStyle.color : option.table.td.textStyle.color;
                td.style.fontFamily = i == 0 ? option.table.header.textStyle.fontFamily : option.table.td.textStyle.fontFamily;
                td.style.fontSize = i == 0 ? option.table.header.textStyle.fontSize : option.table.td.textStyle.fontSize;
                td.style.textAlign = 'center';
                td.innerHTML = !option.dataSet.data[i] ? '' : !option.dataSet.data[i][j] ? '' : option.dataSet.data[i][j];
            }
        }
    }
}

可以看到,在构造函数中,首先将传入的 table 对象 存入到自己的 table 对象中,方便后面使用,如果不想 table 公开,可以声明为私有对象。 然后添加一个 setOption() 的公有方法,方法有一个形参 option ,用于设置 tbale 的样式。接下来的代码就好理解了,根据 option 设置 table 的样式,根据行数和列数动态给 table 添加行和列,显示表格,并设置样式。

一切看起来似乎非常的顺利,不过你有没有发现,我给出的 html 代码中好像并没有那么多的设置啊?这个 setOption() 方法中的那么多设置哪来的?看到这里就是本篇文章的关键之处了。关键!关键!关键!重要的事情说三遍,我们的插件中需要规定一些默认的配置和配置对象,这个配置可以根据项目需要写成通用的样式,这样当用户不需要特殊修改时,报表也会呈现出你想要达到的样子。好了,继续看代码:

var textStyle = {
        color: 'cyan',
        fontFamily: '楷体',
        fontSize: '12px',
    }

    var options = {
        title: {
            text: '',
            x: 'center',
            y: 'middle',
            textStyle: textStyle,
        },
        table: {
            border: '1px solid rgb(0,0,0)',
            sColor: 'rgba(0,0,0,0.1)',
            dColor: 'rgba(21,22,88,0.5)',
            header: {
                textStyle: textStyle,
            },
            td: {
                textStyle: textStyle,
            }
        },
        dataSet: {
            data: '',
            default: {
                columns: 4,
                rows: 6,
            }
        },
    };

我这里只列举了几个简单的样式对象,当你的需求要求越来越高时,这个默认的样式可以完善的越来越详细,并且抽象成更具体的样式对象,就像 echart 那样。那么问题来了,当用户设置了某些样式,而其他样式缺省时,我们的 table 是如何获取到全部的样式属性的呢?这时要编写一个方法,我姑且叫做 copyOption() 方法,代码如下:

 var copyOption = function (source, option) {
        if (!option) {
            option = source;
            return option;
        };
        for (var prop in source) {
            if (typeof (source[prop]) == 'object') {
                if(!!option[prop]){
                    option[prop] = copyOption(source[prop], option[prop]);
                }
                else{
                    option[prop] = source[prop];
                }
            } else {
                if(!option[prop]){
                    option[prop] = source[prop];
                }
            }
        }
        return option;
    }

方法有两个形参,需要复制的源option,用户提供的设置option,最后返回合并完整的option 对象。这个方法中使用到了递归的编程思想,需要注意的是,我们的对象类型是引用类型,不可以直接复制,因为直接复制的是引用地址,这时需要将对象递归调用该函数进行进一步深入复制,这块不理解的小伙伴可以留言,或者搜索一下 JavaScript 深复制,学习一下相关的知识回头再理解这段代码。

以上,就实现了文章开头我所提到的所有功能,虽然功能还不太完善,但是用起来确实方便多了,以后我也会进一步完善。对于这篇文章感兴趣的小伙伴可以跟我一起探讨,多提宝贵意见,感谢大家支持!!!

### UniApp 插件市场资源分包实现 #### 背景说明 UniApp 的插件市场提供了丰富的功能模块,这些模块可以通过 JavaScript 或者原生语言(如 iOS 和 Android 原生代码)编写而成[^1]。然而,在实际开发过程中,可能会面临主包体积过大的问题,尤其是在引入多个插件的情况下。为了优化性能并减少主包大小,可以将部分资源移至分包中。 #### 分包机制概述 分包是指将应用的功能模块拆分为多个独立的小包,只有当用户访问特定页面时才会加载对应的子包内容。这种方式能够有效降低初始加载时间,提升用户体验[^2]。 #### 如何将插件市场中的资源移动到分包? 以下是具体实现方式: 1. **确认插件是否支持动态加载** 需要先判断该插件是否适合被放置于分包之中。对于一些依赖全局初始化或者频繁使用插件来说,可能并不适合作为分包处理。而对于那些仅在某些特殊场景下才需要用到的插件,则非常适合放入分包内[^3]。 2. **调整项目结构** 将目标插件的相关文件复制到指定目录下作为子包的一部分。例如如果打算把某个图表库(l-echart)放进名为`charts`的子包里,那么应该按照如下路径组织: ``` /subPackages/charts/ |- components/ |- l-echart.vue |- pages/ |- chartPage.vue ``` 3. **配置pages.json** 修改项目的 `pages.json` 文件来定义新的分包设置: ```json { "subpackages": [ { "root": "subPackages/charts", "name": "chartsPackage", "pages": ["pages/chartPage"] } ] } ``` 4. **按需导入插件** 在需要使用的地方通过条件编译的方式引入对应组件或方法。比如在一个单独页面上展示柱状图时再加载相关脚本: ```javascript import * as echarts from '@/components/l-echart/echarts.min.js'; export default{ data(){ return {options:{}} }, watch:{ options(newVal){ this.chart.setOption(newVal); } }, mounted(){ const el = document.getElementById('main'); this.chart = echarts.init(el); window.addEventListener('resize', ()=>{ this.chart.resize(); }); } }; ``` 5. **测试与验证** 完成分包操作之后一定要进行全面测试以确保各功能正常运行,并且注意观察打包后的文件尺寸是否有明显改善[^4]。 #### 注意事项 - 动态加载虽然能减小初次启动所需下载的数据量,但也可能导致切换不同模块之间存在短暂延迟。 - 如果某项服务既存在于主线程又位于子线程当中的话,务必保持两者的版本一致性以免引发冲突。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值