Vue3如何在子组件内访问插槽根DOM元素以适配SortableJS
Vue3 中获取插槽根DOM元素适配SortableJS的实现方案
Vue2中我们可以直接将插槽第一个元素作为组件根节点,通过this.$el快速绑定SortableJS,实现代码如下:
export default { render() { return this.$slots.default[0] }, mounted() { Sortable.create(this.$el, {}); } }
Vue3中由于虚拟节点结构、插槽API和组件实例暴露规则的变化,直接调用this.$slots.default()[0]无法直接拿到目标DOM,普通模板ref也只能获取当前组件的外层容器,无法直接获取插槽内部的根元素。可以通过以下两种方案复现Vue2中的能力:
方案1:无额外外层容器实现(完全对齐Vue2用法)
直接通过插槽返回的虚拟节点获取真实DOM,跳过注释、碎片等非元素节点,兼容原生元素和自定义组件插槽场景:
选项API写法
import Sortable from 'sortablejs' export default { mounted() { const slotVNodes = this.$slots.default() // 过滤获取第一个元素类型的虚拟节点 const targetVnode = slotVNodes.find(vnode => vnode.type !== Symbol('v-fgt') && vnode.el?.nodeType === 1) if (!targetVnode) return // 插槽传入自定义组件时,取主动暴露的根元素,否则直接取vnode绑定的DOM const targetEl = targetVnode.component?.exposed?.rootEl || targetVnode.el Sortable.create(targetEl, { // 自定义Sortable配置 }) }, render() { // 直接返回插槽内容,无额外DOM包裹 return this.$slots.default() } }
组合式API写法
<script setup> import { useSlots, onMounted } from 'vue' import Sortable from 'sortablejs' const slots = useSlots() onMounted(() => { const slotVNodes = slots.default() const targetVnode = slotVNodes.find(vnode => vnode.type !== Symbol('v-fgt') && vnode.el?.nodeType === 1) if (!targetVnode) return const targetEl = targetVnode.component?.exposed?.rootEl || targetVnode.el Sortable.create(targetEl, { // 自定义Sortable配置 }) }) // 直接渲染插槽内容,无需外层容器 defineRender(() => slots.default()) </script>
如果插槽传入自定义组件,需要对应组件主动暴露内部根元素:defineExpose({ rootEl: 内部根元素的ref })
方案2:极简兼容方案(推荐)
新增外层容器设置display: contents,该属性会让容器不参与页面布局,视觉和结构上完全等价于直接绑定插槽根元素,代码更易维护:
<template> <div ref="sortableContainer" style="display: contents"> <slot /> </div> </template> <script setup> import { ref, onMounted } from 'vue' import Sortable from 'sortablejs' const sortableContainer = ref() onMounted(() => { Sortable.create(sortableContainer.value, { // 自定义Sortable配置 }) }) </script>
该方案不需要处理复杂的虚拟节点逻辑,也不会对原有页面样式结构产生任何影响,适配绝大多数场景。
内容的提问来源于stack exchange,提问作者Simon M
相关产品推荐
相关产品推荐

