【組件】用原生JS封裝一個Table組件

寫在前面

在寫項目的時候,咱們常常會用一些組件,好比:模態框、表格、分頁等。組件的應用大大減小了項目的開發成本,同時也提升了代碼的質量等。因此,封裝組件成爲了每一個人的必須擁有的技能。本篇文章將使用原生JS封裝一個Table組件。javascript

組件封裝

實現目標

antd中的Table組件爲目標,實現如下功能:css

  • width值控制表格寬度。前端

  • columnsdataSource中的數據相對應,沒有對應數據的地方顯示爲空。java

  • 實現columns中的render函數方法。git

  • columns數據中key值的檢查,沒有或者重複,給出警告。github

  • columnswidth可設置列寬web

  • columnsalign值控制內容在表格中所處的位置。 數組

搭建結構

首先,先搭建一下代碼的結構antd

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)
    }
})()
複製代碼

接下來,咱們來作一個異常處理app

1.若是options不爲對象、columnsdataSource不爲數組,則報錯

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.當columnskey沒有或者重複時,給出警告

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() {

}
複製代碼

建立tabletheadtbody

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

本篇文章以antdTable組件爲目標,封裝了一個本身的Table組件,功能上和antd還有不小的差距,還須要我繼續努力完善它。

文中代碼可能會有不合理或者錯的地方,還但願你們指出來,咱們共同窗習,共同進步~

最後,分享一下個人公衆號,你們快來關注呀~

相關文章
相關標籤/搜索