BootstrapVue b-table分页切换时无法显示获取的列表项
问题描述
使用BootstrapVue的b-table实现外部分页时,切换页面后从REST API获取的列表数据已存在于数组中,但表格无法显示;第一页能正常渲染,切换到后续页面表格空白,返回第一页时数据又能正常加载。控制台监听items数组变化输出正常,调研发现这是b-table的已知限制:
b-table doesn't always know if the items being replaced (specifically when using external pagination) are from the same table or if the items array changes (i.e., to new data altogether). It must clear the selected items array whenever anything changes the table's context (sorting, filtering, pagination, table refresh, etc.).
解决方案
1. 配置外部分页核心属性
b-table的外部分页需要明确告知组件总数据条数,通过server-items-length绑定总条数,让组件识别这是同一表格的分页切换而非数据源完全替换,同时直接绑定组件内置的page-change事件更可靠。
修改模板中的b-table:
<b-table id="my-table1" :items="tableItems" :fields="fields" :busy="isBusy" :per-page="limit" :current-page="currentPage" :server-items-length="totalRows" <!-- 新增:告知组件总数据量 --> stacked="md" table-class="padding" :tbody-tr-class="rowClass" show-empty small responsive hover borderless selectable @page-change="changePage" <!-- 直接绑定内置分页切换事件 --> > </b-table>
2. 简化数据更新逻辑
原代码中先清空数组再延迟赋值的操作,可能导致b-table误判为数据源完全替换,直接浅拷贝赋值即可触发Vue响应式更新:
修改watch及对应方法:
watch: { items: { deep: true, // 监听数组内部元素变化 handler(newItems) { console.log("items changed", newItems); this.tableItems = [...newItems]; // 直接赋值,无需清空+延迟操作 }, }, }, methods: { changePage(page) { this.currentPage = page; this.$emit("pageChange", (page - 1) * this.limit, this.limit); } }
3. 手动触发表格刷新(兜底方案)
如果上述方法仍不生效,可在数据更新后手动调用表格的refresh方法强制渲染:
先给b-table添加ref:
<b-table ref="myTable" <!-- 新增ref用于调用组件方法 --> <!-- 其他属性不变 --> > </b-table>
再修改watch中的处理逻辑:
watch: { items: { deep: true, async handler(newItems) { console.log("items changed", newItems); this.tableItems = [...newItems]; await this.$nextTick(); // 等待DOM更新完成 this.$refs.myTable?.refresh(); // 手动触发表格刷新 }, }, },
4. 同步选中状态避免干扰
根据官方限制,b-table会在分页切换时清除选中项数组,如果绑定了选中状态,需同步更新避免渲染异常:
在模板中添加选中状态绑定及事件:
<b-table :selected="selectedRows" @selected-items-changed="selectedRows = $event" <!-- 其他属性不变 --> > </b-table>
修改后的完整代码示例
模板部分
<b-table ref="myTable" id="my-table1" :items="tableItems" :fields="fields" :busy="isBusy" :per-page="limit" :current-page="currentPage" :server-items-length="totalRows" stacked="md" table-class="padding" :tbody-tr-class="rowClass" show-empty small responsive hover borderless selectable :selected="selectedRows" @selected-items-changed="selectedRows = $event" @page-change="changePage" > </b-table>
脚本部分
props: { fields: { type: Array, default: () => [] }, items: { type: Array, default: () => [] }, totalRows: { type: Number, default: 0 }, isBusy: { type: Boolean, default: false }, }, data() { return { currentPage: 1, limit: 5, tableItems: [], selectedRows: [], }; }, watch: { items: { deep: true, async handler(newItems) { console.log("items changed", newItems); this.tableItems = [...newItems]; await this.$nextTick(); this.$refs.myTable?.refresh(); }, }, }, methods: { changePage(page) { this.currentPage = page; this.$emit("pageChange", (page - 1) * this.limit, this.limit); } }
内容的提问来源于stack exchange,提问作者Abhikrishnan S S

