后台元素UI是PC端流行的Vue.jsUI框架,其组件库基本可以满足大部分常见的业务需求。但有时会有一些比较高的定制化要求,组件本身不一定能满足。我最近在一个项目中遇到了它。很多页面需要用到表格组件el-table。如果el-table-column没有指定宽度,默认会均匀分布到剩余的列中。在列数较多的情况下,如果el-table的宽度限制在容器内,单元格中的内容会换行。不强制换行,内容要么在单元格内滚动,要么溢出,要么被截断。产品期望的效果是:内容保持单行显示,列间距保持一致,表格超出容器允许水平滚动。el-table-column支持设置固定宽度,在内容宽度可预测的情况下也能满足这个需求。问题是如何动态调整列宽以适应内容的宽度。我在官方文档中没有找到这样的选项,应该是组件本身不支持。技术方案于是想出了动态计算内容宽度的方案。网上也有人提到了这个想法。方法是根据内容中的字符数来计算宽度。这种方法有几个局限性:内容必须是不同字符宽度的文本,而且结算结果不够准确。渲染前需要对数据进行操作,不利于解耦。我采用了另一种思路,或者动态计算内容宽度,但是根据实际情况渲染的DOM元素的宽度可以解决以上三个问题。怎么做?通过查看渲染后的DOM元素,发现el-table的header和content分别使用了一个原生的table,每列的宽度是通过colgroup设置的。从这里开始,col的name属性值与对应td的class值一致,这样就可以遍历对应列的所有单元格,找到宽度最大的单元格,为其内容宽度加上边距作为列的宽度。具体实现中如何计算内容宽度?这是比较关键的一步。渲染后的每个单元格都有一个.cell类,使用white-space:nowrap;溢出:自动;设置不允许换行,内容超出后滚动,并设置display:inline-block;计算实际内容宽度。这样就可以通过.cell元素的scrollWidth属性得到最终的宽度。函数adjustColumnWidth(table){constcolgroup=table.querySelector("colgroup");constcolDefs=[...colgroup.querySelectorAll("col")];colDefs.forEach((col)=>{constclsName=col.getAttribute("name");constcells=[...table.querySelectorAll(`td.${clsName}`),...table.querySelectorAll(`th.${clsName}`),];//忽略添加的“leave-alone”类列if(cells[0]?.classList?.contains?.("leave-alone")){return;}constwidthList=cells.map((el)=>{returnel.querySelector(".cell")?.scrollWidth||0;});constmax=Math.max(...widthList);constpadding=32;table.querySelectorAll(`col[name=${clsName}]`).forEach((el)=>{el.setAttribute("width",max+padding);});});}中的探索过程middle比较繁琐,但是最终的代码实现非常简洁。什么时候会触发列宽计算?自然是在组件渲染完成之后。为了方便复用,我采用了vue自定义指令的方式。Vue.directive("fit-columns",{update(){},bind(){},inserted(el){setTimeout(()=>{adjustColumnWidth(el);},300);},componentUpdated(el){el.classList.add("r-table");setTimeout(()=>{adjustColumnWidth(el);},300);},unbind(){},});更进一步,我封装了一个Vue插件,叫做v-fit-columns,已经发布到npm仓库,安装后可以直接使用。安装:npminstallv-fit-columns--save介绍:importVuefrom'vue';从“v-fit-columns”导入插件;Vue.use(插件);使用:
