diff --git a/packages/runtime-vapor/__tests__/for.spec.ts b/packages/runtime-vapor/__tests__/for.spec.ts
index 2b882f95b..9ed969c2d 100644
--- a/packages/runtime-vapor/__tests__/for.spec.ts
+++ b/packages/runtime-vapor/__tests__/for.spec.ts
@@ -7,6 +7,7 @@ import {
renderEffect,
shallowRef,
template,
+ triggerRef,
withDirectives,
} from '../src'
import { makeRender } from './_utils'
@@ -383,7 +384,7 @@ describe('createFor', () => {
expect(host.innerHTML).toBe('')
})
- test.fails('shallowRef source', async () => {
+ test('shallowRef source', async () => {
const list = shallowRef([{ name: '1' }, { name: '2' }, { name: '3' }])
const setList = (update = list.value.slice()) => (list.value = update)
function reverse() {
@@ -435,12 +436,12 @@ describe('createFor', () => {
'
0. 11. 22. 33. 4',
)
- // change
+ // change deep value should not update
list.value[0].name = 'a'
setList()
await nextTick()
expect(host.innerHTML).toBe(
- '0. a1. 22. 33. 4',
+ '0. 11. 22. 33. 4',
)
// remove
@@ -448,7 +449,7 @@ describe('createFor', () => {
setList()
await nextTick()
expect(host.innerHTML).toBe(
- '0. a1. 32. 4',
+ '0. 11. 32. 4',
)
// clear
@@ -456,4 +457,247 @@ describe('createFor', () => {
await nextTick()
expect(host.innerHTML).toBe('')
})
+
+ test('should optimize call frequency during list operations', async () => {
+ let sourceCalledTimes = 0
+ let renderCalledTimes = 0
+ let effectLabelCalledTimes = 0
+ let effectIndexCalledTimes = 0
+
+ const resetCounter = () => {
+ sourceCalledTimes = 0
+ renderCalledTimes = 0
+ effectLabelCalledTimes = 0
+ effectIndexCalledTimes = 0
+ }
+ const expectCalledTimesToBe = (
+ message: string,
+ source: number,
+ render: number,
+ label: number,
+ index: number,
+ ) => {
+ expect(
+ {
+ source: sourceCalledTimes,
+ render: renderCalledTimes,
+ label: effectLabelCalledTimes,
+ index: effectIndexCalledTimes,
+ },
+ message,
+ ).toEqual({ source, render, label, index })
+ resetCounter()
+ }
+
+ const createItem = (
+ (id = 0) =>
+ (label = id) => ({ id: id++, label })
+ )()
+ const createItems = (length: number) =>
+ Array.from({ length }, (_, i) => createItem(i))
+ const list = ref(createItems(100))
+ const length = () => list.value.length
+
+ define(() => {
+ const n1 = createFor(
+ () => (++sourceCalledTimes, list.value),
+ ([item, index]) => {
+ ++renderCalledTimes
+ const span = document.createElement('li')
+ renderEffect(() => {
+ ++effectLabelCalledTimes
+ item.value.label
+ })
+ renderEffect(() => {
+ ++effectIndexCalledTimes
+ index.value
+ })
+ return span
+ },
+ item => item.id,
+ )
+ return n1
+ }).render()
+
+ // Create rows
+ expectCalledTimesToBe('Create rows', 1, length(), length(), length())
+
+ // Update every 10th row
+ for (let i = 0; i < length(); i += 10) {
+ list.value[i].label += 10000
+ }
+ await nextTick()
+ expectCalledTimesToBe('Update every 10th row', 0, 0, length() / 10, 0)
+
+ // Append rows
+ list.value.push(...createItems(100))
+ await nextTick()
+ expectCalledTimesToBe('Append rows', 1, 100, 100, 100)
+
+ // Inserts rows at the beginning
+ const tempLen = length()
+ list.value.unshift(...createItems(100))
+ await nextTick()
+ expectCalledTimesToBe(
+ 'Inserts rows at the beginning',
+ 1,
+ 100,
+ 100,
+ 100 + tempLen,
+ )
+
+ // Inserts rows in the middle
+ const middleIdx = length() / 2
+ list.value.splice(middleIdx, 0, ...createItems(100))
+ await nextTick()
+ expectCalledTimesToBe(
+ 'Inserts rows in the middle',
+ 1,
+ 100,
+ 100,
+ 100 + middleIdx,
+ )
+
+ // Swap rows
+ const temp = list.value[1]
+ list.value[1] = list.value[length() - 2]
+ list.value[length() - 2] = temp
+ await nextTick()
+ expectCalledTimesToBe('Swap rows', 1, 0, 0, 2)
+
+ // Remove rows
+ list.value.splice(1, 1)
+ list.value.splice(length() - 2, 1)
+ await nextTick()
+ expectCalledTimesToBe('Remove rows', 1, 0, 0, length() - 1)
+
+ // Clear rows
+ list.value = []
+ await nextTick()
+ expectCalledTimesToBe('Clear rows', 1, 0, 0, 0)
+ })
+
+ test('should optimize call frequency during list operations with shallowRef', async () => {
+ let sourceCalledTimes = 0
+ let renderCalledTimes = 0
+ let effectLabelCalledTimes = 0
+ let effectIndexCalledTimes = 0
+
+ const resetCounter = () => {
+ sourceCalledTimes = 0
+ renderCalledTimes = 0
+ effectLabelCalledTimes = 0
+ effectIndexCalledTimes = 0
+ }
+ const expectCalledTimesToBe = (
+ message: string,
+ source: number,
+ render: number,
+ label: number,
+ index: number,
+ ) => {
+ expect(
+ {
+ source: sourceCalledTimes,
+ render: renderCalledTimes,
+ label: effectLabelCalledTimes,
+ index: effectIndexCalledTimes,
+ },
+ message,
+ ).toEqual({ source, render, label, index })
+ resetCounter()
+ }
+
+ const createItem = (
+ (id = 0) =>
+ (label = id) => ({ id: id++, label: shallowRef(label) })
+ )()
+ const createItems = (length: number) =>
+ Array.from({ length }, (_, i) => createItem(i))
+ const list = shallowRef(createItems(100))
+ const length = () => list.value.length
+
+ define(() => {
+ const n1 = createFor(
+ () => (++sourceCalledTimes, list.value),
+ ([item, index]) => {
+ ++renderCalledTimes
+ const span = document.createElement('li')
+ renderEffect(() => {
+ ++effectLabelCalledTimes
+ item.value.label.value
+ })
+ renderEffect(() => {
+ ++effectIndexCalledTimes
+ index.value
+ })
+ return span
+ },
+ item => item.id,
+ )
+ return n1
+ }).render()
+
+ // Create rows
+ expectCalledTimesToBe('Create rows', 1, length(), length(), length())
+
+ // Update every 10th row
+ for (let i = 0; i < length(); i += 10) {
+ list.value[i].label.value += 10000
+ }
+ await nextTick()
+ expectCalledTimesToBe('Update every 10th row', 0, 0, length() / 10, 0)
+
+ // Append rows
+ list.value.push(...createItems(100))
+ triggerRef(list)
+ await nextTick()
+ expectCalledTimesToBe('Append rows', 1, 100, 100, 100)
+
+ // Inserts rows at the beginning
+ const tempLen = length()
+ list.value.unshift(...createItems(100))
+ triggerRef(list)
+ await nextTick()
+ expectCalledTimesToBe(
+ 'Inserts rows at the beginning',
+ 1,
+ 100,
+ 100,
+ 100 + tempLen,
+ )
+
+ // Inserts rows in the middle
+ const middleIdx = length() / 2
+ list.value.splice(middleIdx, 0, ...createItems(100))
+ triggerRef(list)
+ await nextTick()
+ expectCalledTimesToBe(
+ 'Inserts rows in the middle',
+ 1,
+ 100,
+ 100,
+ 100 + middleIdx,
+ )
+
+ // Swap rows
+ const temp = list.value[1]
+ list.value[1] = list.value[length() - 2]
+ list.value[length() - 2] = temp
+ triggerRef(list)
+ await nextTick()
+ expectCalledTimesToBe('Swap rows', 1, 0, 0, 2)
+
+ // Remove rows
+ list.value.splice(1, 1)
+ list.value.splice(length() - 2, 1)
+ triggerRef(list)
+ await nextTick()
+ expectCalledTimesToBe('Remove rows', 1, 0, 0, length() - 1)
+
+ // Clear rows
+ list.value = []
+ await nextTick()
+ expectCalledTimesToBe('Clear rows', 1, 0, 0, 0)
+ })
})
diff --git a/packages/runtime-vapor/src/apiCreateFor.ts b/packages/runtime-vapor/src/apiCreateFor.ts
index e0a945d1a..6033c3617 100644
--- a/packages/runtime-vapor/src/apiCreateFor.ts
+++ b/packages/runtime-vapor/src/apiCreateFor.ts
@@ -4,7 +4,6 @@ import {
effectScope,
isReactive,
shallowRef,
- triggerRef,
} from '@vue/reactivity'
import { isArray, isObject, isString } from '@vue/shared'
import {