你的理解完全正确!在不重构现有布局代码(不迁移到 Flex)的前提下,监听搜索框高度变化并动态重新计算表格高度,确实是风险最小、改动成本最低的方案。
针对存量代码,我们核心要解决的问题就是:
表格高度 = 总高度(100vh) - Logo高度 - 分页高度 - 间距 - 动态的搜索框高度
在 Vue 3 中,最现代且性能最好的实现方式是使用 ResizeObserver API。它比旧的 window.onresize 强大得多,因为它可以专门监听某个 DOM 元素(搜索框)的尺寸变化。
以下是具体的实现代码:
核心思路
- Ref 绑定 :给包裹搜索框的容器打上
ref。 - 监听变化 :在
onMounted中启动ResizeObserver监听这个容器。 - 动态计算 :每当搜索框高度变了,就更新表格的
height属性。
代码实现
假设你原来的 CSS 是写死的 height: calc(100vh - 200px)(这 200px 通常包含了 Logo、分页和 padding),现在的逻辑变成了:
vue
<template>
<div class="page-container">
<div class="header" style="height: 60px">Logo</div>
<div ref="searchBoxRef" class="search-box">
<el-input
v-model="query"
type="textarea"
:autosize="{ minRows: 1, maxRows: 6 }"
placeholder="请输入内容撑开高度"
/>
</div>
<div class="table-wrapper">
<el-table :data="tableData" :height="tableHeight" border>
<el-table-column prop="date" label="Date" />
<el-table-column prop="name" label="Name" />
</el-table>
</div>
<div class="pagination" style="height: 50px">
<el-pagination total="100" />
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue'
const query = ref('')
const tableData = [] // 你的数据
// 1. 定义 ref 拿到 DOM
const searchBoxRef = ref(null)
// 2. 定义表格高度,给个默认值防止闪烁
const tableHeight = ref('500px')
// 3. 定义页面上除了搜索框和表格之外的【固定高度之和】
// 这里需要你自己算一下:Logo高度(60) + 分页高度(50) + 上下Padding/Margin(假设40) = 150
// 如果这一块也很难算,后面我有更暴力的减法方案
const FIXED_OFFSET = 150
let observer = null
onMounted(() => {
if (!searchBoxRef.value) return
// 实例化 ResizeObserver
observer = new ResizeObserver((entries) => {
for (const entry of entries) {
// 获取搜索框现在的实际高度 (offsetHeight 包含 border 和 padding)
// 注意:entry.contentRect 不包含 padding,建议直接读取 DOM 的 offsetHeight 更稳妥
const searchHeight = entry.target.offsetHeight
// 动态计算表格高度
// 这里的逻辑是:屏幕总高 - 其他固定杂项 - 搜索框高度
tableHeight.value = `calc(100vh - ${FIXED_OFFSET}px - ${searchHeight}px)`
}
})
// 开始监听
observer.observe(searchBoxRef.value)
})
onBeforeUnmount(() => {
// 组件销毁时记得断开监听,防止内存泄漏
if (observer) observer.disconnect()
})
</script>
<style scoped>
.page-container {
padding: 20px;
/* 存量代码可能没有 flex,保持现状即可 */
}
.search-box {
/* 只需要保证它没有定死 height,能被内容撑开即可 */
margin-bottom: 10px;
}
.pagination {
margin-top: 10px;
}
</style>
关键细节说明
-
为什么用
ResizeObserver而不是watch监听输入内容?- 如果你监听
query字符串的变化来推算高度,会极其复杂(要计算行高、字体大小、换行逻辑)。 ResizeObserver是浏览器原生 API,它直接告诉你"这个div现在有多高",不管是因为字多了换行,还是因为浏览器窗口变窄导致换行,它都能捕获,这是最精准的。
- 如果你监听
-
关于
FIXED_OFFSET(固定高度差) 的计算如果你的存量代码里,除了搜索框和表格,其他部分(Logo、分页、各种 margin)高度非常乱,很难算出一个精准的数字,你可以采用**"全减法"**策略。
修改
tableHeight的计算逻辑:javascript// 在 onMounted 里面算,不需要自己手动加减 const windowHeight = window.innerHeight // 窗口总高 const headerHeight = document.querySelector('.header').offsetHeight // 动态获取 Logo区高度 const paginationHeight = document.querySelector('.pagination').offsetHeight // 动态获取 分页区高度 const margins = 40 // 预估的间距 // 然后每次 ResizeObserver 回调里: tableHeight.value = `${windowHeight - headerHeight - paginationHeight - margins - searchHeight}px`(注:这种全动态减法需要监听
window.resize来更新windowHeight,稍微麻烦点。如果你能确定其他区域高度大致固定,建议直接写死FIXED_OFFSET常量,维护起来最简单。) -
Element Plus 表格的
:heightElement Plus 的
el-table组件支持直接传入 CSS 字符串(如calc(xxx))。当你更新tableHeight.value时,表格会自动触发重绘,调整内部滚动条的高度,完全符合你的需求。
总结
对于存量代码,"ResizeObserver + 动态 calc" 是性价比最高的方案。它不需要你改动外层 HTML 结构,只需要给搜索框加个钩子,然后动态改一下 Table 的高度属性即可。