写在前面
在写项目的时候,我们经常会用一些组件,比如:模态框、表格、分页等。组件的应用大大减少了项目的开发成本,同时也提高了代码的质量等。所以,封装组件成为了每个人的必须拥有的技能。本篇文章将使用原生JS
封装一个Table
组件。
组件封装
实现目标
以antd
中的Table
组件为目标,实现以下功能:
传
width
值控制表格宽度。columns
与dataSource
中的数据相对应,没有对应数据的地方显示为空。实现
columns
中的render
函数方法。对
columns
数据中key
值的检查,没有或者重复,给出警告。columns
中width
可设置列宽columns
中align
值控制内容在表格中所处的位置。
搭建结构
首先,先搭建一下代码的结构
window.Table(function(){
class Table {
constructor(options) {
// 获取所需要数据
this.columns = options.columns;
this.dataSource = options.dataSource;
this.width = options.width;
this.init(); // 调用初始化函数
}
// 初始化
init() {
}
}
return function proxy(options = {}) {
options = Object.assign({
columns: [],
dataSource: [],
width: '80%',
}, options);
return new Table(options)
}
})()
复制代码
接下来,我们来做一个异常处理
1.如果options
不为对象、columns
、dataSource
不为数组,则报错
if(!Array.isArray(options?.columns)) {
throw new Error('error:columns must be a array');
}
if(!Array.isArray(options?.dataSource)) {
throw new Error('error:dataSource must be a array');
}
if(options === null || typeof options !== "object") {
throw new Error('error:options must be a object');
}
复制代码
2.当columns
的key
没有或者重复时,给出警告
if(!Array.isArray(options?.columns)) {
throw new Error('error:columns must be a array');
} else {
for(let i = 0; i < options?.columns.length; i++) {
for(let j = i + 1; j < options?.columns.length; j++) {
if(!options?.columns[i]?.key) {
console.error('warning:Each item in columns should have a key');
break;
} else {
if(options?.columns[i]?.key === options?.columns[j]?.key) {
console.error('warning:The key for each item in columns should be unique');
break;
}
}
}
}
}
复制代码
既然是一个组件,那么就应该有对应的UI
样式,所以接下来这一步,需要创建节点
init() {
this.createElement();
}
// 创建dom节点函数,两个参数: 1.节点类型 2.css样式
create(type, cssText) {
let ele = document.createElement(type);
ele.style.cssText = cssText;
return ele;
}
createElement() {
}
复制代码
创建table
、thead
、tbody
createElement() {
// table
this.$TABLE = this.create('table', `
width: ${this.width};
`);
// thead
this.$TABLE_HEAD = this.createHead();
// tbody
this.$TABLE_BODY = this.createBody();
// 向table中添加thead
this.$TABLE.appendChild(this.$TABLE_HEAD);
// 向body中插入table
document.body.appendChild(this.$TABLE);
}
复制代码
创建thead函数
createHead() {
let { columns } = this;
let THEAD_TH = null;
// 创建thead
this.$THEAD = this.create("thead", `background: #e3e3e3`);
// 创建tr
this.$THEAD_TR = this.create("tr");
// 遍历创建th,并且向th中添加内容
for(let i = 0; i< columns.length; i++) {
THEAD_TH = this.create("th", `
border: 1px solid #999;
width: ${item?.width};
`)
THEAD_TH.innerHTML = columns[i].title;
}
// 遍历向tr中添加th节点
for(let j = 0; j < THEAD_TH; j++) {
this.$THEAD_TR.appendChild(THEAD_TH.appendChild(THEAD_TH[j]));
}
// 向thead中添加tr节点
this.$THEAD.appendChild(this.$THEAD_TR);
// 返回thead
return this.$THEAD;
}
复制代码
现在的样式
创建tbody
函数
createBody() {
let { dataSource, columns } = this;
// 初始化数据
let TBODY_TR = null,
TBODY_TD = null,
TBODY = this.create("tbody");
// 遍历生成tbody节点下的tr和td
for(let i = 0; i < dataSource.length;i++) {
// 创建tr
TBODY_TR = this.create('tr');
for(let j = 0;j < columns.length;j++) {
// 创建td
TBODY_TD = this.create('td', `
border: 1px solid #999;
text-align: ${columns[j]?.align};
`);
// 传入的render是函数
if(columns[j]?.render && typeof columns[j]?.render === "function") {
// 执行render函数,传入行值和列值,并且获得返回值
let render = columns[j]?.render(dataSource[i][columns[j]?.dataIndex], dataSource[i]);
// 如果返回值是一个dom节点,则向td里插入节点
if(typeof render === "object") {
TBODY_TD.appendChild(render);
} else { // 否则直接innerHTML
TBODY_TD.innerHTML = render;
}
} else { // 没有传render时,直接插入对应值
TBODY_TD.innerHTML = dataSource[i][columns[j].dataIndex] || ''
}
// 向tr中插入td
TBODY_TR.appendChild(TBODY_TD);
// 向tbody中插入tr
TBODY.appendChild(TBODY_TR);
}
}
return TBODY;
}
复制代码
现在,就完成了我们的基本功能,来看一下效果
上面的效果,我们传入的数据是这样的
const columns = [{
title: '姓名',
dataIndex: 'name',
align: 'center',
key: 'name',
}, {title: '年龄',
dataIndex: 'age',
}, {title: '工作',
dataIndex: 'job',
key: 'name',
}, {
title: '操作',
key: 'action',
render: (text, record) => {
return create('a', 'color: blue', {record})
}
}]
function create(type, cssText, data) {
let element = document.createElement(type);
element.style.cssText = cssText;
element.innerHTML = '删除';
element.onclick = click.bind(this, data.record);
return element;
}
function click(record) {
console.log(record);
}
const dataSource = [{
name: '小红',
age: '111',
job: '前端'
}, {
age: '111'
}, {
}, {
name: '小红1',
job1: '前端'
}]
Table({columns, dataSource})
复制代码
这份数据,很显然,是会报错的
修改一下columns
的数据
const columns = [{
title: '姓名',
dataIndex: 'name',
align: 'center',
key: 'name',
}, {title: '年龄',
dataIndex: 'age',
key: 'age',
}, {title: '工作',
dataIndex: 'job',
key: 'job',
}, {
title: '操作',
key: 'action',
render: (text, record) => {
return create('a', 'color: blue', {record})
}
}]
复制代码
这就不报错了,至此,我们完成了目标的几个功能。
总结
文中代码已经上传到github
中,地址为:table
本篇文章以antd
的Table
组件为目标,封装了一个自己的Table
组件,功能上和antd
还有不小的差距,还需要我继续努力完善它。
文中代码可能会有不合理或者错的地方,还希望大家指出来,我们共同学习,共同进步~
最后,分享一下我的公众号,大家快来关注呀~