动态组件拖拽排序

67 阅读2分钟

动态组件拖拽排序

背景

可以抽象为:对于A、B、C等多个组件,组件的个数是不固定的,每个组件都有其定制内容(例如:每个组件里面都有不同的echarts图像),用户可以进行拖拽排序,并保存该顺序,下次进入页面或者刷新页面时能够保持上一次的组件顺序进行展示。拖拽功能选择采用vuedraggable实现。

实现

动态组件

在vue中,正常的写法是:

<A v-if="showA"></A>
<B v-if="showB"></B>
<C v-if="showC"></C>

但是这样写就固定了组件的展示顺序,无法满足需求。这里就需要用到动态组件。动态组件通过 Vue 的 <component> 元素和特殊的 is attribute 实现的:

<!-- currentTab 改变时组件也改变 -->
<component :is="tabs[currentTab]"></component>

动态组件可以让组件和JS逻辑结合起来,而JS中的对象是很灵活的,这样就是实现根据后端返回的数据顺序来展示对应组件。

<component :is="componentMap[element]" v-bind="componentParams[element]" :list="list" @handleDownload="handleDownload" />

v-bind="componentParams[element]"这一部分是动态传参,为每一个组件传递对应的参数

image.png

拖拽时避免重新渲染

如果只是像上面实现方式,在拖拽的时候会发现组件中的echarts图像会重新渲染,在对应组件的生命周期钩子中打印log可以发现每个组件也会重新挂载。但是我们肯定不想要重新渲染,影响用户体验,于是可以使用vue中的KeepAlive保持挂载。

<KeepAlive>
    <component :is="componentMap[element]" v-bind="componentParams[element]" :list="list" @handleDownload="handleDownload" />
</KeepAlive>

遇到的问题

用了KeepAlive之后,发现第一次拖拽组件的时候还是会重新渲染,之后不会。考虑到可能是vuedraggable指定key出错的问题。

<draggable 
    v-model="subscriptionList" 
    drag-class="drag-class" 
    :item-key="element" 
    handle=".mover" 
    @end="handleDragEnd" 
    class="flex-box flex_d-column gap_20"
>
    <template #item="{ element }">
        <div>
            <KeepAlive>
                <component :is="componentMap[element]" v-bind="componentParams[element]" :list="list" @handleDownload="handleDownload" />
            </KeepAlive>
        </div>
    </template>
</draggable>

因为subscriptionList是字符串数组,每个字符是唯一的,所以我指定的item-keyelement,但是这样的写法不对,正确的写法是:item-key="(element: string) => element",写成一个函数的形式,成功解决上述问题。