Skip to content

Commit b7c0e52

Browse files
committed
fix: Rewrite view (re-)assignment logic (#743)
1 parent 547cf52 commit b7c0e52

File tree

1 file changed

+74
-36
lines changed

1 file changed

+74
-36
lines changed

packages/vue-virtual-scroller/src/components/RecycleScroller.vue

Lines changed: 74 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -277,8 +277,10 @@ export default {
277277
created () {
278278
this.$_startIndex = 0
279279
this.$_endIndex = 0
280+
// Visible views by their key
280281
this.$_views = new Map()
281-
this.$_unusedViews = new Map()
282+
// Pools of recycled views, by view type
283+
this.$_recycledPools = new Map()
282284
this.$_scrollDirty = false
283285
this.$_lastUpdateScrollPosition = 0
284286
@@ -318,7 +320,17 @@ export default {
318320
},
319321
320322
methods: {
321-
addView (pool, index, item, key, type) {
323+
getRecycledPool (type) {
324+
const recycledPools = this.$_recycledPools
325+
let recycledPool = recycledPools.get(type)
326+
if (!recycledPool) {
327+
recycledPool = []
328+
recycledPools.set(type, recycledPool)
329+
}
330+
return recycledPool
331+
},
332+
333+
createView (pool, index, item, key, type) {
322334
const nr = markRaw({
323335
id: uid++,
324336
index,
@@ -335,18 +347,31 @@ export default {
335347
return view
336348
},
337349
338-
unuseView (view, fake = false) {
339-
const unusedViews = this.$_unusedViews
340-
const type = view.nr.type
341-
let unusedPool = unusedViews.get(type)
342-
if (!unusedPool) {
343-
unusedPool = []
344-
unusedViews.set(type, unusedPool)
350+
getRecycledView (type) {
351+
const recycledPool = this.getRecycledPool(type)
352+
if (recycledPool && recycledPool.length) {
353+
const view = recycledPool.pop()
354+
view.nr.used = true
355+
return view
356+
} else {
357+
return null
345358
}
346-
unusedPool.push(view)
347-
if (!fake) {
348-
view.nr.used = false
349-
view.position = -9999
359+
},
360+
361+
removeAndRecycleView (view) {
362+
const type = view.nr.type
363+
const recycledPool = this.getRecycledPool(type)
364+
recycledPool.push(view)
365+
view.nr.used = false
366+
view.position = -9999
367+
this.$_views.delete(view.nr.key)
368+
},
369+
370+
removeAndRecycleAllViews () {
371+
this.$_views.clear()
372+
this.$_recycledPools.clear()
373+
for (let i = 0, l = this.pool.length; i < l; i++) {
374+
this.removeAndRecycleView(this.pool[i])
350375
}
351376
},
352377
@@ -397,7 +422,7 @@ export default {
397422
}
398423
},
399424
400-
updateVisibleItems (checkItem, checkPositionDiff = false) {
425+
updateVisibleItems (itemsChanged, checkPositionDiff = false) {
401426
const itemSize = this.itemSize
402427
const gridItems = this.gridItems || 1
403428
const itemSecondarySize = this.itemSecondarySize || itemSize
@@ -408,7 +433,6 @@ export default {
408433
const count = items.length
409434
const sizes = this.sizes
410435
const views = this.$_views
411-
const unusedViews = this.$_unusedViews
412436
const pool = this.pool
413437
const itemIndexByKey = this.itemIndexByKey
414438
let startIndex, endIndex
@@ -522,19 +546,30 @@ export default {
522546
523547
const continuous = startIndex <= this.$_endIndex && endIndex >= this.$_startIndex
524548
525-
// Unuse views that are no longer visible
526-
if (continuous) {
549+
if (this.$_continuous !== continuous) {
550+
if (continuous) {
551+
views.clear()
552+
unusedViews.clear()
553+
for (let i = 0, l = pool.length; i < l; i++) {
554+
view = pool[i]
555+
this.unuseView(view)
556+
}
557+
}
558+
this.$_continuous = continuous
559+
} else if (continuous) {
527560
for (let i = 0, l = pool.length; i < l; i++) {
528561
view = pool[i]
529562
if (view.nr.used) {
530563
// Update view item index
531564
if (checkItem) {
532-
view.nr.index = itemIndexByKey[view.item[keyField]]
565+
view.nr.index = items.findIndex(
566+
item => keyField ? item[keyField] === view.item[keyField] : item === view.item,
567+
)
533568
}
534569
535570
// Check if index is still in visible range
536571
if (
537-
view.nr.index == null ||
572+
view.nr.index === -1 ||
538573
view.nr.index < startIndex ||
539574
view.nr.index >= endIndex
540575
) {
@@ -546,11 +581,13 @@ export default {
546581
547582
const unusedIndex = continuous ? null : new Map()
548583
549-
let item, type
584+
let item, type, unusedPool
550585
let v
551586
for (let i = startIndex; i < endIndex; i++) {
587+
const elementSize = itemSize || sizes[i].size
588+
if (!elementSize) continue
552589
item = items[i]
553-
const key = keyField ? item[keyField] : item
590+
const key = keyField ? item[keyField] : i
554591
if (key == null) {
555592
throw new Error(`Key is ${key} on item (keyField is '${keyField}')`)
556593
}
@@ -561,17 +598,20 @@ export default {
561598
continue
562599
}
563600
564-
type = item[typeField]
565-
566-
let unusedPool = unusedViews.get(type)
567-
let newlyUsedView = false
568-
569601
// No view assigned to item
570602
if (!view) {
603+
type = item[typeField]
604+
unusedPool = unusedViews.get(type)
605+
571606
if (continuous) {
572607
// Reuse existing view
573608
if (unusedPool && unusedPool.length) {
574609
view = unusedPool.pop()
610+
view.item = item
611+
view.nr.used = true
612+
view.nr.index = i
613+
view.nr.key = key
614+
view.nr.type = type
575615
} else {
576616
view = this.addView(pool, i, item, key, type)
577617
}
@@ -588,7 +628,13 @@ export default {
588628
}
589629
590630
view = unusedPool[v]
631+
view.item = item
632+
view.nr.used = true
633+
view.nr.index = i
634+
view.nr.key = key
635+
view.nr.type = type
591636
unusedIndex.set(type, v + 1)
637+
v++
592638
}
593639
594640
// Assign view to item
@@ -601,16 +647,8 @@ export default {
601647
602648
newlyUsedView = true
603649
} else {
604-
// View already assigned to item
605-
if (!view.nr.used) {
606-
view.nr.used = true
607-
view.nr.index = i
608-
newlyUsedView = true
609-
if (unusedPool) {
610-
const index = unusedPool.indexOf(view)
611-
if (index !== -1) unusedPool.splice(index, 1)
612-
}
613-
}
650+
view.nr.used = true
651+
view.item = item
614652
}
615653
616654
// Always set item in case it's a new object with the same key

0 commit comments

Comments
 (0)