Skip to content

Commit 5f6cd32

Browse files
authored
test(runtime-vapor): fix shallowRef in v-for (#280)
1 parent b962aa5 commit 5f6cd32

File tree

2 files changed

+248
-5
lines changed

2 files changed

+248
-5
lines changed

packages/runtime-vapor/__tests__/for.spec.ts

+248-4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
renderEffect,
88
shallowRef,
99
template,
10+
triggerRef,
1011
withDirectives,
1112
} from '../src'
1213
import { makeRender } from './_utils'
@@ -383,7 +384,7 @@ describe('createFor', () => {
383384
expect(host.innerHTML).toBe('<!--for-->')
384385
})
385386

386-
test.fails('shallowRef source', async () => {
387+
test('shallowRef source', async () => {
387388
const list = shallowRef([{ name: '1' }, { name: '2' }, { name: '3' }])
388389
const setList = (update = list.value.slice()) => (list.value = update)
389390
function reverse() {
@@ -435,25 +436,268 @@ describe('createFor', () => {
435436
'<li>0. 1</li><li>1. 2</li><li>2. 3</li><li>3. 4</li><!--for-->',
436437
)
437438

438-
// change
439+
// change deep value should not update
439440
list.value[0].name = 'a'
440441
setList()
441442
await nextTick()
442443
expect(host.innerHTML).toBe(
443-
'<li>0. a</li><li>1. 2</li><li>2. 3</li><li>3. 4</li><!--for-->',
444+
'<li>0. 1</li><li>1. 2</li><li>2. 3</li><li>3. 4</li><!--for-->',
444445
)
445446

446447
// remove
447448
list.value.splice(1, 1)
448449
setList()
449450
await nextTick()
450451
expect(host.innerHTML).toBe(
451-
'<li>0. a</li><li>1. 3</li><li>2. 4</li><!--for-->',
452+
'<li>0. 1</li><li>1. 3</li><li>2. 4</li><!--for-->',
452453
)
453454

454455
// clear
455456
setList([])
456457
await nextTick()
457458
expect(host.innerHTML).toBe('<!--for-->')
458459
})
460+
461+
test('should optimize call frequency during list operations', async () => {
462+
let sourceCalledTimes = 0
463+
let renderCalledTimes = 0
464+
let effectLabelCalledTimes = 0
465+
let effectIndexCalledTimes = 0
466+
467+
const resetCounter = () => {
468+
sourceCalledTimes = 0
469+
renderCalledTimes = 0
470+
effectLabelCalledTimes = 0
471+
effectIndexCalledTimes = 0
472+
}
473+
const expectCalledTimesToBe = (
474+
message: string,
475+
source: number,
476+
render: number,
477+
label: number,
478+
index: number,
479+
) => {
480+
expect(
481+
{
482+
source: sourceCalledTimes,
483+
render: renderCalledTimes,
484+
label: effectLabelCalledTimes,
485+
index: effectIndexCalledTimes,
486+
},
487+
message,
488+
).toEqual({ source, render, label, index })
489+
resetCounter()
490+
}
491+
492+
const createItem = (
493+
(id = 0) =>
494+
(label = id) => ({ id: id++, label })
495+
)()
496+
const createItems = (length: number) =>
497+
Array.from({ length }, (_, i) => createItem(i))
498+
const list = ref(createItems(100))
499+
const length = () => list.value.length
500+
501+
define(() => {
502+
const n1 = createFor(
503+
() => (++sourceCalledTimes, list.value),
504+
([item, index]) => {
505+
++renderCalledTimes
506+
const span = document.createElement('li')
507+
renderEffect(() => {
508+
++effectLabelCalledTimes
509+
item.value.label
510+
})
511+
renderEffect(() => {
512+
++effectIndexCalledTimes
513+
index.value
514+
})
515+
return span
516+
},
517+
item => item.id,
518+
)
519+
return n1
520+
}).render()
521+
522+
// Create rows
523+
expectCalledTimesToBe('Create rows', 1, length(), length(), length())
524+
525+
// Update every 10th row
526+
for (let i = 0; i < length(); i += 10) {
527+
list.value[i].label += 10000
528+
}
529+
await nextTick()
530+
expectCalledTimesToBe('Update every 10th row', 0, 0, length() / 10, 0)
531+
532+
// Append rows
533+
list.value.push(...createItems(100))
534+
await nextTick()
535+
expectCalledTimesToBe('Append rows', 1, 100, 100, 100)
536+
537+
// Inserts rows at the beginning
538+
const tempLen = length()
539+
list.value.unshift(...createItems(100))
540+
await nextTick()
541+
expectCalledTimesToBe(
542+
'Inserts rows at the beginning',
543+
1,
544+
100,
545+
100,
546+
100 + tempLen,
547+
)
548+
549+
// Inserts rows in the middle
550+
const middleIdx = length() / 2
551+
list.value.splice(middleIdx, 0, ...createItems(100))
552+
await nextTick()
553+
expectCalledTimesToBe(
554+
'Inserts rows in the middle',
555+
1,
556+
100,
557+
100,
558+
100 + middleIdx,
559+
)
560+
561+
// Swap rows
562+
const temp = list.value[1]
563+
list.value[1] = list.value[length() - 2]
564+
list.value[length() - 2] = temp
565+
await nextTick()
566+
expectCalledTimesToBe('Swap rows', 1, 0, 0, 2)
567+
568+
// Remove rows
569+
list.value.splice(1, 1)
570+
list.value.splice(length() - 2, 1)
571+
await nextTick()
572+
expectCalledTimesToBe('Remove rows', 1, 0, 0, length() - 1)
573+
574+
// Clear rows
575+
list.value = []
576+
await nextTick()
577+
expectCalledTimesToBe('Clear rows', 1, 0, 0, 0)
578+
})
579+
580+
test('should optimize call frequency during list operations with shallowRef', async () => {
581+
let sourceCalledTimes = 0
582+
let renderCalledTimes = 0
583+
let effectLabelCalledTimes = 0
584+
let effectIndexCalledTimes = 0
585+
586+
const resetCounter = () => {
587+
sourceCalledTimes = 0
588+
renderCalledTimes = 0
589+
effectLabelCalledTimes = 0
590+
effectIndexCalledTimes = 0
591+
}
592+
const expectCalledTimesToBe = (
593+
message: string,
594+
source: number,
595+
render: number,
596+
label: number,
597+
index: number,
598+
) => {
599+
expect(
600+
{
601+
source: sourceCalledTimes,
602+
render: renderCalledTimes,
603+
label: effectLabelCalledTimes,
604+
index: effectIndexCalledTimes,
605+
},
606+
message,
607+
).toEqual({ source, render, label, index })
608+
resetCounter()
609+
}
610+
611+
const createItem = (
612+
(id = 0) =>
613+
(label = id) => ({ id: id++, label: shallowRef(label) })
614+
)()
615+
const createItems = (length: number) =>
616+
Array.from({ length }, (_, i) => createItem(i))
617+
const list = shallowRef(createItems(100))
618+
const length = () => list.value.length
619+
620+
define(() => {
621+
const n1 = createFor(
622+
() => (++sourceCalledTimes, list.value),
623+
([item, index]) => {
624+
++renderCalledTimes
625+
const span = document.createElement('li')
626+
renderEffect(() => {
627+
++effectLabelCalledTimes
628+
item.value.label.value
629+
})
630+
renderEffect(() => {
631+
++effectIndexCalledTimes
632+
index.value
633+
})
634+
return span
635+
},
636+
item => item.id,
637+
)
638+
return n1
639+
}).render()
640+
641+
// Create rows
642+
expectCalledTimesToBe('Create rows', 1, length(), length(), length())
643+
644+
// Update every 10th row
645+
for (let i = 0; i < length(); i += 10) {
646+
list.value[i].label.value += 10000
647+
}
648+
await nextTick()
649+
expectCalledTimesToBe('Update every 10th row', 0, 0, length() / 10, 0)
650+
651+
// Append rows
652+
list.value.push(...createItems(100))
653+
triggerRef(list)
654+
await nextTick()
655+
expectCalledTimesToBe('Append rows', 1, 100, 100, 100)
656+
657+
// Inserts rows at the beginning
658+
const tempLen = length()
659+
list.value.unshift(...createItems(100))
660+
triggerRef(list)
661+
await nextTick()
662+
expectCalledTimesToBe(
663+
'Inserts rows at the beginning',
664+
1,
665+
100,
666+
100,
667+
100 + tempLen,
668+
)
669+
670+
// Inserts rows in the middle
671+
const middleIdx = length() / 2
672+
list.value.splice(middleIdx, 0, ...createItems(100))
673+
triggerRef(list)
674+
await nextTick()
675+
expectCalledTimesToBe(
676+
'Inserts rows in the middle',
677+
1,
678+
100,
679+
100,
680+
100 + middleIdx,
681+
)
682+
683+
// Swap rows
684+
const temp = list.value[1]
685+
list.value[1] = list.value[length() - 2]
686+
list.value[length() - 2] = temp
687+
triggerRef(list)
688+
await nextTick()
689+
expectCalledTimesToBe('Swap rows', 1, 0, 0, 2)
690+
691+
// Remove rows
692+
list.value.splice(1, 1)
693+
list.value.splice(length() - 2, 1)
694+
triggerRef(list)
695+
await nextTick()
696+
expectCalledTimesToBe('Remove rows', 1, 0, 0, length() - 1)
697+
698+
// Clear rows
699+
list.value = []
700+
await nextTick()
701+
expectCalledTimesToBe('Clear rows', 1, 0, 0, 0)
702+
})
459703
})

packages/runtime-vapor/src/apiCreateFor.ts

-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import {
44
effectScope,
55
isReactive,
66
shallowRef,
7-
triggerRef,
87
} from '@vue/reactivity'
98
import { isArray, isObject, isString } from '@vue/shared'
109
import {

0 commit comments

Comments
 (0)