]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
test(runtime-vapor): fix shallowRef in v-for (#280)
authorRizumu Ayaka <rizumu@ayaka.moe>
Fri, 27 Sep 2024 17:53:29 +0000 (01:53 +0800)
committerGitHub <noreply@github.com>
Fri, 27 Sep 2024 17:53:29 +0000 (01:53 +0800)
packages/runtime-vapor/__tests__/for.spec.ts
packages/runtime-vapor/src/apiCreateFor.ts

index 2b882f95bf0662142fd3f964678f4c6969329dda..9ed969c2d2c0b216ffd040ff5769006a3096f194 100644 (file)
@@ -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('<!--for-->')
   })
 
-  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', () => {
       '<li>0. 1</li><li>1. 2</li><li>2. 3</li><li>3. 4</li><!--for-->',
     )
 
-    // change
+    // change deep value should not update
     list.value[0].name = 'a'
     setList()
     await nextTick()
     expect(host.innerHTML).toBe(
-      '<li>0. a</li><li>1. 2</li><li>2. 3</li><li>3. 4</li><!--for-->',
+      '<li>0. 1</li><li>1. 2</li><li>2. 3</li><li>3. 4</li><!--for-->',
     )
 
     // remove
@@ -448,7 +449,7 @@ describe('createFor', () => {
     setList()
     await nextTick()
     expect(host.innerHTML).toBe(
-      '<li>0. a</li><li>1. 3</li><li>2. 4</li><!--for-->',
+      '<li>0. 1</li><li>1. 3</li><li>2. 4</li><!--for-->',
     )
 
     // clear
@@ -456,4 +457,247 @@ describe('createFor', () => {
     await nextTick()
     expect(host.innerHTML).toBe('<!--for-->')
   })
+
+  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)
+  })
 })
index e0a945d1a0dea8e5e1db17ac65357ec1f0422290..6033c3617a0d578d604d0f6e56c8c2a960057b0c 100644 (file)
@@ -4,7 +4,6 @@ import {
   effectScope,
   isReactive,
   shallowRef,
-  triggerRef,
 } from '@vue/reactivity'
 import { isArray, isObject, isString } from '@vue/shared'
 import {