Quellcode durchsuchen

fix(runtime-vapor): prevent v-for crash on looping through null or undefined array (#14328)

Co-authored-by: daiwei <daiwei521@126.com>
Ivan vor 3 Monaten
Ursprung
Commit
e77b6e1fb9

+ 33 - 0
packages/runtime-vapor/__tests__/componentSlots.spec.ts

@@ -2186,5 +2186,38 @@ describe('component: slots', () => {
           '<div><!--slot--></div>',
       )
     })
+
+    test('should work with null and undefined', async () => {
+      const loop = ref<number[] | null | undefined>(undefined)
+
+      let instance: any
+      const Child = () => {
+        instance = currentInstance
+        return template('child')()
+      }
+
+      const { render } = define({
+        setup() {
+          return createComponent(Child, null, {
+            $: [
+              () =>
+                createForSlots(loop.value as any, (item, i) => ({
+                  name: item,
+                  fn: () => template(item + i)(),
+                })),
+            ],
+          })
+        },
+      })
+      render()
+
+      expect(instance.slots).toEqual({})
+      loop.value = [1]
+      await nextTick()
+      expect(instance.slots).toHaveProperty('1')
+      loop.value = null
+      await nextTick()
+      expect(instance.slots).toEqual({})
+    })
   })
 })

+ 34 - 0
packages/runtime-vapor/__tests__/for.spec.ts

@@ -691,6 +691,40 @@ describe('createFor', () => {
     expectCalledTimesToBe('Clear rows', 1, 0, 0, 0)
   })
 
+  test('should work with null and undefined', async () => {
+    const list = ref<{ name: string }[] | null | undefined>(undefined)
+
+    const { host } = define(() => {
+      const n1 = createFor(
+        () => list.value as any,
+        (item, key, index) => {
+          const span = document.createElement('li')
+          renderEffect(() => {
+            span.innerHTML = `${key.value}. ${item.value.name}`
+
+            // index should be undefined if source is not an object
+            expect(index.value).toBe(undefined)
+          })
+          return span
+        },
+        item => item.name,
+      )
+      return n1
+    }).render()
+
+    expect(host.innerHTML).toBe('<!--for-->')
+
+    // set to valid array
+    list.value = [{ name: '1' }]
+    await nextTick()
+    expect(host.innerHTML).toBe('<li>0. 1</li><!--for-->')
+
+    // null
+    list.value = null
+    await nextTick()
+    expect(host.innerHTML).toBe('<!--for-->')
+  })
+
   describe('readonly source', () => {
     test('should not allow mutation', () => {
       const arr = readonly(reactive([{ foo: 1 }]))

+ 2 - 0
packages/runtime-vapor/src/apiCreateFor.ts

@@ -609,6 +609,8 @@ function normalizeSource(source: any): ResolvedSource {
         values[i] = source[keys[i]]
       }
     }
+  } else {
+    values = []
   }
   return {
     values,