ant-design-vue中table自定義列

1. 使用背景

在項目中使用ant-vuea-table控制項過程中,需要顯示序號列或者在列中顯示圖片,超鏈,按鈕等UI資訊。經過查詢文檔customCellcustomRender可以實現以上需求,比如實現如下表格數據渲染

樣式預覽

2. slots&scopedSlots作用

在查看文檔過程中,在類型一欄中經常看到 xxx|slot |slot-scope 這樣的描述資訊。比如customRender在文檔中的描述資訊

customRender | 生成複雜數據的渲染函數.. | **Function(text, record, index) {}|slot-scope**

在最初一直以為在列中可以是如下配置的

// 公眾號:小院不小 date 20210205 wx:464884492
const tableColumn = [
      {
        title: '遊戲名稱',
        dataIndex: 'title',
        customRender:'xxslot'
      }
]

這樣定義後執行npm run serve在瀏覽器會出現customRender is not function 的錯誤資訊。以及後來看到有如下寫法

// 公眾號:小院不小 date 20210205 wx:464884492
const tableColumn = [
      {
        title: '遊戲名稱',
        dataIndex: 'title',
        scopedSlots: {
          customRender: "customRender"
        }
      }
]

還有很長一段時間不明白scopedSlots這個對象的屬性為啥是customRender, 還有其他的什麼屬性嗎?當時知識還不完善沒有理解到文檔上使用 columns 時,可以通過該屬性配置支援 slot-scope 的屬性的含義

雖然知道怎麼用了,但還是有必要了解下它是如何運行的。我們知道在vue中可以通過this.$slotsthis.$scopedSlots分別訪問靜態插槽和作用域插槽。在文件components\table\index.jsx中可以找到組件庫對scopedSlotsslots轉換成具體函數的過程,程式碼如下

 // 公眾號:小院不小 date 20210205 wx:464884492
 ...
 // 獲取插槽
 const { $slots, $scopedSlots } = this;
 // 轉換靜態插槽
 Object.keys(slots).forEach(key => {
      const name = slots[key];
      if (column[key] === undefined && $slots[name]) {
        column[key] = $slots[name].length === 1 ? $slots[name][0] : $slots[name];
      }
    });
 // 轉換動態插槽  
 Object.keys(scopedSlots).forEach(key => {
  const name = scopedSlots[key];
  if (column[key] === undefined && $scopedSlots[name]) {
    column[key] = $scopedSlots[name];
  }
 });

從以上程式碼也可以知道,如果您定義如下的列配置,自定插槽會失效,以下程式碼該列會全部顯示123

// 公眾號:小院不小 date 20210205 wx:464884492
{
    title: "customRender|slot-scope",
    dataIndex: '',
    customRender: () => 123,
    scopedSlots: {
      customRender: "customRender"
    }
}

也就是說customRender定義成函數的優先順序高於作用域插槽

3. customCell

customCell影響的是vnode中的屬性資訊,你可以改變當前列的樣式等相關資訊,在文件 components\vc-table\src\TableCell.jsx 對應程式碼片段

// 公眾號:小院不小 date 20210205 wx:464884492
...
 if (column.customCell) {
  tdProps = mergeProps(tdProps, column.customCell(record, index));
}
...
 return (
  <BodyCell class={cellClassName} {...tdProps}>
    {indentText}
    {expandIcon}
    {text}
  </BodyCell>
);    

所以這個對象可以傳遞值可以參考vue官方文檔深入數據對象中的描述。你可以返回如下對改變當前列的字體大小和顏色

// 公眾號:小院不小 date 20210205 wx:464884492
 return {
    style: {
      color: 'red',
      fontSize: '14px'
    }
 }

也可通過如下改變顯示的內容

// 公眾號:小院不小 date 20210205 wx:464884492
return {
  domProps: {
      innerHTML: record.title + "#" + (index + 1)
    }
}

4. customRender

customRender也可以影響當前列的顯示資訊,不過它更靈活。可以返回一段jsx獲取返回一個類似customCell一樣的屬性資訊。不過從程式碼來看,它只接收一下屬性attrspropsclassstylechildren,而且它的優先順序也沒有customCell優先順序高。customRender可以是一個插槽,也可以是一個函數。
當作為插槽使用時程式碼應該如下所示

// 公眾號:小院不小 date 20210205 wx:464884492
[{
  title: "customRender|slot-scope",
  dataIndex: '',
  scopedSlots: {
    customRender: "customRender"
  }
},{
  title: "customRender|slot-scope",
  dataIndex: '',
  slots: {
    customRender: "customRender"
  }
}]

從上邊了解到的插槽知識可以知道作用域插槽的優先順序高於靜態插槽也就是說,在一個列中分別配置了鍵值相等的靜態插槽和作用域插槽,將優先顯示作用域插槽的內容
當作為函數使用時,程式碼應該如下所示

// 公眾號:小院不小 date 20210205 wx:464884492
[{
  title: '遊戲特點',
  dataIndex: 'desc',
  customRender: (text, record, index) => {
    if (index == 1) {
      return <div> {text} <span style="color:blue"> @小院不小</span></div>
    }
    return {
      attrs:{},
      props:{},
      class:{},
      style:{},
      children: text
    }
  }
}]

兩種返回值組件通過isInvalidRenderCellText函數判斷。判斷是否是jsx的方式主要程式碼如下

// 公眾號:小院不小 date 20210205 wx:464884492
function isValidElement(element) {
  return (
    element &&
    typeof element === 'object' &&
    'componentOptions' in element &&
    'context' in element &&
    element.tag !== undefined
  ); 
}

通過上邊的說明,我們就能很好的使用customRender屬性了。不過我們還是有必要了解一下,這段屬性對應源程式碼邏輯。在文件components\vc-table\src\TableCell.jsx 對應的程式碼片段如下

// 公眾號:小院不小 date 20210205 wx:464884492
if (customRender) {
  text = customRender(text, record, index, column);
  if (isInvalidRenderCellText(text)) {
    tdProps.attrs = text.attrs || {};
    tdProps.props = text.props || {};
    tdProps.class = text.class;
    tdProps.style = text.style;
    colSpan = tdProps.attrs.colSpan;
    rowSpan = tdProps.attrs.rowSpan;
    text = text.children;
  }
}

if (column.customCell) {
  tdProps = mergeProps(tdProps, column.customCell(record, index));
}

5. 總結

ant的組件很靈活,很多需要通過擴展來實現一些特殊的功能.customRendercustomCell都可以實現自定義列資訊。在什麼場景下使用,還需要根據不同業務訴求。比如我要改變列字體,顏色等,我們就優先考慮customCell.根據上面的介紹這裡有一個面試題程式碼如下

// 公眾號:小院不小 date 20210205 wx:464884492
{
  title: "自定義列",
  dataIndex: '',
  customRender:()=>'函數渲染'
   scopedSlots: {
    customRender: "scopedSlots"
  },
  slots: {
    customRender: "slots"
  }
}

請問列自定義列最終渲染的內容是

  • A 函數渲染
  • B scopedSlots
  • C slots

如果想知道答案或需要Demo源碼請掃描下方的二維碼,關注公眾號[小院不小],回復ant-table獲取.

公眾號