Vue中实现React.Fragment,多个duo根元素的解决方案
2021-03-29 17:33

前言

近日,在工作中实现table绘制报表的功能,采用的时vue框架,由于报表展现形式需要多种rowspan,colspan来固定样式。而elementUI,vxe-table的span-method方法对于实现复杂表格并不适用。所以最终使用vue 的jsx进行自己绘制table。

最终结果长这样:

image.png


vue2 现状

Fragment是Vue3.0中的新特性,而目前vue2中的组件唯一根元素仍是硬性要求。

报表jsx渲染中与唯一根元素规则的冲突。

对于报表渲染的是实现中,由于要进行rowspan合并,所以最好的方式是遍历数据,每次遍历第一个td进行rospan=2设置,之后渲染第一行得分,再渲染第二行排名。

然而实际开中的代码如下:

props:{
        lists:{
            type:Array,
            default:()=>{
                return [];
            }
        },
        tps:{
            type:Array,
            default:()=>{
                return ['银川','吴忠','石嘴山','中卫','宁东','固原'];
            }
        }
    },
render(){
        return (
            <div style="height:100%;overflow:auto;">
            <table style="width:100%;border:1px solid #ccc;border-collapse:collapse;" class="rp-table" border >
                <tr>
                    <th colspan="3" class="rp-header">&nbsp;&nbsp;</th>
                    {
                        this.tps.map(it=>{
                            return <th class="rp-header">{it}</th>
                        })
                    }
                </tr>
                {
                    
                    this.lists.map((it,i)=>{
                            return (
                              <template>
                                <tr>
                                        {
                                            i==1?<td rowspan={this.lists.length-1}>专业管理</td>:""
                                        }
                                        <td rowspan="2" >{ it.dwmc }</td>
                                        <td colspan={i==0?2:1}>得分2</td>
                                        {
                                            this.tps.map((t,k)=>{
                                                return <td >
                                                        {
                                                            it.value&&it.value.length>k?it.value[k].zbdf:""
                                                        }
                                                        </td>
                                            })
                                        }
                                 </tr>
                                 <tr>
                                        
                                        <td colspan={i==0?2:1}>排名2</td>
                                        {
                                            this.tps.map((t,k)=>{
                                                return <td >
                                                        {
                                                            it.value&&it.value.length>k?it.value[k].zbpm:""
                                                        }
                                                        </td>
                                            })
                                        }
                                    </tr>
                                 </template>
                            )
                    })
                    
                }
                
            </table>
            </div>
        )
    }

lists是传入的数据。

         最开始使用的是<template>将两个tr包围起来,形成唯一根元素,但是页面上就不会渲染此段代码。

之后百度了会,改为tbody,这个tbody一定范围内可行。基本效果都能实现,唯独最左侧的“专业管理”rowspan多个效果不好使,因为两个 tr包围在一个tbody里面,那么rowspan的最大值也就成了2了。也算是失败了。

        最后又找了一通,发现了好东西“vue-fragment”,声称实现了类似于React.Fragment的功能,是一个指令组件,在渲染层通过<fragmeng>标签蒙蔽vue的唯一根元素规则,在dom操作底层将最外层包裹元素删除。

试了一下,可以完美实现最开始的效果。

        本来一切挺美好,但当报表在dialog弹窗中展示时,美好破灭了。vue-fragment虽然蒙蔽了vue的创建元素规则,可却没处理vue删除dom元素时的规则,导致vue找不到享要删除元素的引用,他一报错,dialog关闭失灵了。

        

        至此,通过Fragment的实现方案破灭了。虽然说可以通过进一步完善vue-fragment来解决这一问题,但是这个方案需要细研究vue的底层源码,耗时长久,不可取。

        最后,在经过一番头脑风暴后,终于想出了一个替代方案:

        那就是将lists的里面的数据进行重复,然后在array.map遍历中通过if返回不同的tr,这样就遵守了vue的唯一根元素规则,并且也实现自己享要的效果。

computed:{
        dobleList(){
            let arr = [];
            for(let i =0 ;i<this.lists.length;i++){
                arr.push(this.lists[i]);
                arr.push(this.lists[i]);
            }
            return arr;
        }
    },

    先写一个computed,将lists数据进行双倍处理。

    然后再改造render方法。

{
                    
                    this.dobleList.map((it,i)=>{
                        if(i%2==0){
                            return (
                                    <tr>
                                        {
                                            i/2==1?<td rowspan={(this.lists.length-1)*2+''}>专业管理</td>:""
                                        }
                                        <td rowspan="2" >{ it.dwmc }</td>
                                        <td colspan={i/2==0?2:1}>得分2</td>
                                        {
                                            this.tps.map((t,k)=>{
                                                return <td >
                                                        {
                                                            it.value&&it.value.length>k?it.value[k].zbdf:""
                                                        }
                                                        </td>
                                            })
                                        }
                                    </tr>
                                    
                            )
                        }else{
                            return (
                                <tr>
                                        
                                        <td colspan={(i-1)/2==0?2:1}>排名2</td>
                                        {
                                            this.tps.map((t,k)=>{
                                                return <td >
                                                        {
                                                            it.value&&it.value.length>k?it.value[k].zbpm:""
                                                        }
                                                        </td>
                                            })
                                        }
                                    </tr>
                            )
                        }
                    })
                    
                }

通过对index/2的判断,原来的数据返回第一列tr,复制的第二个数据用来返回第二行数据。效果完美实现,也能享受到vue的数据响应,页面自动刷新的福利。

反想

举一反三,通过这次解决方案。可以总结出,对于实际开发中的复杂功能实现,多个根元素的解决方案都可以依据此方案。两个根元素就把数据复制一遍,三个根元素就把数据复制三遍,。。。。最终就能达到我们想要的唯一根元素的目的。

原创文章,转载请注明来自:妹纸前端-www.webfront-js.com.
阅读(446)
辛苦了,打赏喝个咖啡
微信
支付宝
妹纸前端
妹纸前端工作室 | 文章不断更新中
京ICP备16005385号-1