问题
我们的项目是一个后台管理系统,其中涉及到大量的表格,用的技术是el-table,为了让页面总是能按一屏的高度展示,所以el-table这个高度可以变化的组件就适合来做页面的自适应伸展,主要是通过动态计算El-table的高度,将页面下边的空白。
页面的结构如下:
<template>
<div>
<el-container>
<el-header class="header"> Header </el-header>
<el-container>
<el-container>
<el-aside width="200px" class="aside">Aside</el-aside>
<el-main class="main">
<el-header class="header">有时候有有时候没有 </el-header>
<el-table :height="tableHeight" :data="tableData" style="width: 100%">
<el-table-column prop="date" label="Date" width="180" />
<el-table-column prop="name" label="Name" width="180" />
<el-table-column prop="address" label="Address" />
</el-table>
<el-pagination layout="prev, pager, next" :total="50" />
</el-main>
</el-container>
<el-footer class="footer">Footer</el-footer>
</el-container>
</el-container>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String,
},
data() {
return {
tableHeight: 300,
tableData: [],
}
},
mounted() {
const HEADER_HEIGHT = 60
const HEADER_INNER_HEIGHT = 60
const FOOTER_HEIGHT = 60
const PAGINATION_HEIGHT = 32
this.tableHeight =
window.innerHeight -
HEADER_HEIGHT -
HEADER_INNER_HEIGHT -
FOOTER_HEIGHT -
PAGINATION_HEIGHT;
console.log(this.tableHeight)
},
}
</script>
<style scoped>
.header {
background-color: aquamarine;
}
.aside {
height: calc(100vh - 120px);
background-color: blueviolet;
}
.main {
border: 1px solid gray;
}
.footer {
position: fixed;
width: 100%;
bottom: 0;
background-color: brown;
}
</style>
为了让页面始终展示一屏,就要动态的去计算el-table的高度。如果只是将高度的计算放在mounted中,就会发现并不起作用。这是为什么呢?
这就涉及到el-table的height到底是怎么用的问题了,我们来看下height的用法,通过查看element-ui的packages\table\src\table.vue文件,我们可以找到唯一使用props中传递的height的地方是在watch中:
height: {
immediate: true,
handler(value) {
this.layout.setHeight(value);
}
},
在这里,当height通过props传递时,会立即执行一次
this.layout.setHeight(value)
在packages\table\src\table-layout.js文件中,这个方法中核心代码如下:
setHeight(value, prop = 'height') {
const el = this.table.$el;
this.height = value;
// 这里是在mounted中修改height不生效的关键原因,第一次调用setHeight在watch的immediate中,
// 也就是created阶段,但是此时dom还没有创建,所以setHeight被放在了nextTick中。
// 当第二次调用也就是在mounted中修改高度,触发了高度的watch,此时dom已创建,所以同步执行高度赋值
if (!el && (value || value === 0)) return Vue.nextTick(() => this.setHeight(value, prop));
if (typeof value === 'number') {
el.style[prop] = value + 'px';
this.updateElsHeight();
} else if (typeof value === 'string') {
el.style[prop] = value;
this.updateElsHeight();
}
}
先取得table.$el,也就是
<div class="el-table">
这是el-table渲染到页面上的最外层div,将这个div的height设置成value。最后调用updateElsHeight(),这个函数里面主要是对子元素的高度进行了更新。
所以,理论上说,只要我们在代码中对el-table的高度进行了更改,el-table就会自动响应,那为什么在mounted中写的计算并没有生效呢?
原理
这个关键点就是我在上面setHeight注释中写的:
// 这里是在mounted中修改height不生效的关键原因,第一次调用setHeight在watch的immediate中,
// 也就是created阶段,但是此时dom还没有创建,所以setHeight被放在了nextTick中。
// 当第二次调用也就是在mounted中修改高度,触发了高度的watch,此时dom已创建,所以同步执行高度赋值
if (!el && (value || value === 0)) return Vue.nextTick(() => this.setHeight(value, prop));
由此我们可以看出,只要我们可以保证,在create阶段创建的nextTick执行完毕之后,再去修改表格的高度,表格就会立即响应,为了达到这个效果,我们的代码必须写到另一个nextTick中,这样才能保证它排序在create创建的nextTick之后。我们来改下代码:
mounted() {
const HEADER_HEIGHT = 60
const HEADER_INNER_HEIGHT = 60
const FOOTER_HEIGHT = 60
const PAGINATION_HEIGHT = 32
this.$nextTick(() => {
this.tableHeight =
window.innerHeight -
HEADER_HEIGHT -
HEADER_INNER_HEIGHT -
FOOTER_HEIGHT -
PAGINATION_HEIGHT
console.log(this.tableHeight)
})
},
刷新页面,就可以看到表格高度正确展示了
为了自适应页面大小,可以加上,resize的事件监听:
methods: {
setTableHeight() {
const HEADER_HEIGHT = 60
const HEADER_INNER_HEIGHT = 60
const FOOTER_HEIGHT = 60
const PAGINATION_HEIGHT = 32
this.tableHeight =
window.innerHeight -
HEADER_HEIGHT -
HEADER_INNER_HEIGHT -
FOOTER_HEIGHT -
PAGINATION_HEIGHT
console.log(this.tableHeight)
},
},
mounted() {
this.$nextTick(() => this.setTableHeight())
window.addEventListener('resize', this.setTableHeight)
},
这样就完美的完成了这个功能。
演示代码: issue-resolve/src/components/TableDemo.vue at main · crane1/issue-resolve