Arco Design Layout 中使用 ResizeBox 实现可拖拽侧边栏
问题
a-layout-sider 虽然有 width 属性可以设置宽度,但没有拖拽事件 来动态调整宽度。直接使用 a-resize-box 替换会导致布局混乱。
解决方案
核心要点 :必须使用 <a-layout :has-sider="true"> 包裹 a-resize-box。
vue
<a-layout :has-sider="true">
<a-resize-box
:directions="['right']"
:width="sidebarWidthImmediate"
@moving="handleSidebarResize"
@moving-end="handleSidebarResizeEnd"
>
<div>侧边栏内容</div>
</a-resize-box>
<a-layout-content>
主内容区
</a-layout-content>
</a-layout>
性能优化:双变量设计
问题
如果只用一个变量,拖拽过程中会频繁触发子组件更新和计算属性重新计算,导致卡顿。
方案
使用两个变量分离视觉反馈和数据传递:
typescript
// 视觉反馈变量:高频更新,只影响 ResizeBox
const sidebarWidthImmediate = ref(200)
// 数据传递变量:低频更新,用于子组件和计算属性
const sidebarWidth = ref(200)
function handleSidebarResize(size: { width: number; height: number }) {
// 拖拽过程中:只更新视觉反馈变量
sidebarWidthImmediate.value = Math.max(minSidebarWidth, size.width)
}
function handleSidebarResizeEnd() {
// 拖拽结束时:同步数据传递变量,触发一次子组件更新
sidebarWidth.value = sidebarWidthImmediate.value
}
性能对比
| 方案 | 拖拽时子组件渲染 | 性能表现 |
|---|---|---|
| 单变量 | 高频(每秒数十次) | ❌ 卡顿 |
| 双变量 | 低频(结束时1次) | ✅ 流畅 |
完整示例
vue
<template>
<a-layout>
<a-layout-header>Header</a-layout-header>
<a-layout :has-sider="true">
<a-resize-box
:directions="['right']"
:width="sidebarWidthImmediate"
@moving="handleSidebarResize"
@moving-end="handleSidebarResizeEnd"
>
<div class="sidebar">侧边栏内容</div>
</a-resize-box>
<a-layout-content>
<MainContent
:sidebar-width="sidebarWidth"
:width="contentWidth"
/>
</a-layout-content>
</a-layout>
</a-layout>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
const minSidebarWidth = 200
const sidebarWidthImmediate = ref(minSidebarWidth) // 视觉反馈变量
const sidebarWidth = ref(minSidebarWidth) // 数据传递变量
const resizerWidth = 6
const containerWidth = ref(1200)
const contentWidth = computed(() => {
return containerWidth.value - sidebarWidth.value - resizerWidth
})
function handleSidebarResize(size: { width: number; height: number }) {
sidebarWidthImmediate.value = Math.max(minSidebarWidth, size.width)
}
function handleSidebarResizeEnd() {
sidebarWidth.value = sidebarWidthImmediate.value
}
</script>
注意事项
- 必须设置
has-sider="true":否则布局会混乱 - 宽度计算需减去拖拽条宽度:通常为 6px
- 设置最小宽度限制:防止侧边栏过小
- 使用双变量模式:避免拖拽时频繁触发子组件更新