Skip to content

Commit f3f42f2

Browse files
authored
perf(b-table, b-table-lite): improve render performance (closes #4211, #4155) (#4213)
1 parent b852bba commit f3f42f2

File tree

2 files changed

+90
-9
lines changed

2 files changed

+90
-9
lines changed

src/components/table/helpers/mixin-tbody-row.js

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -85,28 +85,54 @@ export default {
8585
const hasDetailsSlot = this.hasNormalizedSlot(detailsSlotName)
8686
const formatted = this.getFormattedValue(item, field)
8787
const key = field.key
88+
// We only uses the helper components for sticky columns to
89+
// improve performance of BTable/BTableLite by reducing the
90+
// total number of vue instances created during render
91+
const cellTag = field.stickyColumn
92+
? field.isRowHeader
93+
? BTh
94+
: BTd
95+
: field.isRowHeader
96+
? 'th'
97+
: 'td'
98+
const cellVariant =
99+
item._cellVariants && item._cellVariants[key]
100+
? item._cellVariants[key]
101+
: field.variant || null
88102
const data = {
89103
// For the Vue key, we concatenate the column index and
90104
// field key (as field keys could be duplicated)
91105
// TODO: Although we do prevent duplicate field keys...
92106
// So we could change this to: `row-${rowIndex}-cell-${key}`
93107
key: `row-${rowIndex}-cell-${colIndex}-${key}`,
94108
class: [field.class ? field.class : '', this.getTdValues(item, key, field.tdClass, '')],
95-
props: {
96-
stackedHeading: this.isStacked ? field.label : null,
97-
stickyColumn: field.stickyColumn,
98-
variant:
99-
item._cellVariants && item._cellVariants[key]
100-
? item._cellVariants[key]
101-
: field.variant || null
102-
},
109+
props: {},
103110
attrs: {
104111
'aria-colindex': String(colIndex + 1),
105112
...(field.isRowHeader
106113
? this.getThValues(item, key, field.thAttr, 'row', {})
107114
: this.getTdValues(item, key, field.tdAttr, {}))
108115
}
109116
}
117+
if (field.stickyColumn) {
118+
// We are using the helper BTd or BTh
119+
data.props = {
120+
stackedHeading: this.isStacked ? field.label : null,
121+
stickyColumn: field.stickyColumn,
122+
variant: cellVariant
123+
}
124+
} else {
125+
// Using native TD or TH element, so we need to
126+
// add in the attributes and variant class
127+
data.attrs['data-label'] =
128+
this.isStacked && !isUndefinedOrNull(field.label) ? toString(field.label) : null
129+
data.attrs.role = field.isRowHeader ? 'rowheader' : 'cell'
130+
data.attrs.scope = field.isRowHeader ? 'row' : null
131+
// Add in the variant class
132+
if (cellVariant) {
133+
data.class.push(`${this.dark ? 'bg' : 'table'}-${cellVariant}`)
134+
}
135+
}
110136
const slotScope = {
111137
item: item,
112138
index: rowIndex,
@@ -135,7 +161,7 @@ export default {
135161
$childNodes = [h('div', {}, [$childNodes])]
136162
}
137163
// Render either a td or th cell
138-
return h(field.isRowHeader ? BTh : BTd, data, [$childNodes])
164+
return h(cellTag, data, [$childNodes])
139165
},
140166
renderTbodyRow(item, rowIndex) {
141167
// Renders an item's row (or rows if details supported)
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { mount } from '@vue/test-utils'
2+
import { BTable } from './table'
3+
import { BTd } from './td'
4+
import { BTh } from './th'
5+
6+
const items = [{ a: 1, b: 2, c: 3 }, { a: 4, b: 5, c: 6 }]
7+
const fields = [
8+
{ key: 'a', stickyColumn: true, isRowHeader: true },
9+
{ key: 'b', stickyColumn: true },
10+
'c'
11+
]
12+
13+
describe('table > sticky columns', () => {
14+
it('has expected classes when sticky column is enabled', async () => {
15+
const wrapper = mount(BTable, {
16+
propsData: {
17+
responsive: true,
18+
items: items,
19+
fields: fields
20+
}
21+
})
22+
23+
expect(wrapper).toBeDefined()
24+
expect(wrapper.is(BTable)).toBe(true)
25+
expect(wrapper.is('div')).toBe(true)
26+
expect(wrapper.classes()).toContain('table-responsive')
27+
const table = wrapper.find('table')
28+
expect(table.classes()).toContain('table')
29+
expect(table.classes()).toContain('b-table')
30+
31+
const trs = wrapper.findAll('tbody > tr')
32+
expect(trs.length).toBe(2)
33+
34+
const cells = trs.at(0).findAll('th, td')
35+
expect(cells.length).toBe(3)
36+
37+
// First column should be BTh with sticky classes
38+
expect(cells.at(0).is(BTh)).toBe(true)
39+
expect(cells.at(0).is('th')).toBe(true)
40+
expect(cells.at(0).classes()).toContain('b-table-sticky-column')
41+
42+
// Second column should be BTd with sticky classes
43+
expect(cells.at(1).is(BTd)).toBe(true)
44+
expect(cells.at(1).is('td')).toBe(true)
45+
expect(cells.at(1).classes()).toContain('b-table-sticky-column')
46+
47+
// Third column should be td
48+
expect(cells.at(2).is(BTd)).toBe(false)
49+
expect(cells.at(2).is(BTh)).toBe(false)
50+
expect(cells.at(2).is('td')).toBe(true)
51+
expect(cells.at(2).classes()).not.toContain('b-table-sticky-column')
52+
53+
wrapper.destroy()
54+
})
55+
})

0 commit comments

Comments
 (0)