فهرست منبع

fix(vFor): avoid eager evaluation of destructure defaults

daiwei 2 هفته پیش
والد
کامیت
42444be6b6

+ 1 - 1
packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap

@@ -283,7 +283,7 @@ export function render(_ctx) {
   const n0 = _createFor(() => (_ctx.list), (_for_item0) => {
   const n0 = _createFor(() => (_ctx.list), (_for_item0) => {
     const n2 = t0()
     const n2 = t0()
     const x2 = _txt(n2)
     const x2 = _txt(n2)
-    _renderEffect(() => _setText(x2, _toDisplayString(_getDefaultValue(_for_item0.value.foo, _ctx.bar) + _ctx.bar + _ctx.baz + _getDefaultValue(_for_item0.value.baz[0], _ctx.quux) + _ctx.quux)))
+    _renderEffect(() => _setText(x2, _toDisplayString(_getDefaultValue(_for_item0.value.foo, () => (_ctx.bar)) + _ctx.bar + _ctx.baz + _getDefaultValue(_for_item0.value.baz[0], () => (_ctx.quux)) + _ctx.quux)))
     return n2
     return n2
   }, undefined, 8)
   }, undefined, 8)
   return n0
   return n0

+ 2 - 2
packages/compiler-vapor/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap

@@ -431,7 +431,7 @@ const t0 = _template(" ")
 export function render(_ctx) {
 export function render(_ctx) {
   const n2 = _createAssetComponent("Comp", null, (_slotProps0) => {
   const n2 = _createAssetComponent("Comp", null, (_slotProps0) => {
     const n0 = t0()
     const n0 = t0()
-    _renderEffect(() => _setText(n0, _toDisplayString(_getDefaultValue(_slotProps0.foo, 1))))
+    _renderEffect(() => _setText(n0, _toDisplayString(_getDefaultValue(_slotProps0.foo, () => (1)))))
     return n0
     return n0
   }, true)
   }, true)
   return n2
   return n2
@@ -445,7 +445,7 @@ const t0 = _template(" ")
 export function render(_ctx) {
 export function render(_ctx) {
   const n2 = _createAssetComponent("Comp", null, (_slotProps0) => {
   const n2 = _createAssetComponent("Comp", null, (_slotProps0) => {
     const n0 = t0()
     const n0 = t0()
-    _renderEffect(() => _setText(n0, _toDisplayString(_getDefaultValue(_slotProps0.foo[0], 1) + _getDefaultValue(_slotProps0.baz.qux, 2))))
+    _renderEffect(() => _setText(n0, _toDisplayString(_getDefaultValue(_slotProps0.foo[0], () => (1)) + _getDefaultValue(_slotProps0.baz.qux, () => (2)))))
     return n0
     return n0
   }, true)
   }, true)
   return n2
   return n2

+ 4 - 2
packages/compiler-vapor/__tests__/transforms/vFor.spec.ts

@@ -401,9 +401,11 @@ describe('compiler: v-for', () => {
       </div>`,
       </div>`,
     )
     )
     expect(code).matchSnapshot()
     expect(code).matchSnapshot()
-    expect(code).toContain(`_getDefaultValue(_for_item0.value.foo, _ctx.bar)`)
     expect(code).toContain(
     expect(code).toContain(
-      `_getDefaultValue(_for_item0.value.baz[0], _ctx.quux)`,
+      `_getDefaultValue(_for_item0.value.foo, () => (_ctx.bar))`,
+    )
+    expect(code).toContain(
+      `_getDefaultValue(_for_item0.value.baz[0], () => (_ctx.quux))`,
     )
     )
     expect(ir.block.dynamic.children[0].operation).toMatchObject({
     expect(ir.block.dynamic.children[0].operation).toMatchObject({
       type: IRNodeTypes.FOR,
       type: IRNodeTypes.FOR,

+ 3 - 3
packages/compiler-vapor/__tests__/transforms/vSlot.spec.ts

@@ -297,7 +297,7 @@ describe('compiler: transform slot', () => {
     expect(code).contains(
     expect(code).contains(
       `_createAssetComponent("Comp", null, (_slotProps0) =>`,
       `_createAssetComponent("Comp", null, (_slotProps0) =>`,
     )
     )
-    expect(code).contains(`_getDefaultValue(_slotProps0.foo, 1)`)
+    expect(code).contains(`_getDefaultValue(_slotProps0.foo, () => (1))`)
   })
   })
 
 
   test('slot prop nested default value', () => {
   test('slot prop nested default value', () => {
@@ -309,8 +309,8 @@ describe('compiler: transform slot', () => {
     expect(code).contains(
     expect(code).contains(
       `_createAssetComponent("Comp", null, (_slotProps0) =>`,
       `_createAssetComponent("Comp", null, (_slotProps0) =>`,
     )
     )
-    expect(code).contains(`_getDefaultValue(_slotProps0.foo[0], 1)`)
-    expect(code).contains(`_getDefaultValue(_slotProps0.baz.qux, 2)`)
+    expect(code).contains(`_getDefaultValue(_slotProps0.foo[0], () => (1))`)
+    expect(code).contains(`_getDefaultValue(_slotProps0.baz.qux, () => (2))`)
   })
   })
 
 
   test('slot prop rest with computed keys preserved', () => {
   test('slot prop rest with computed keys preserved', () => {

+ 2 - 2
packages/compiler-vapor/src/generators/for.ts

@@ -319,10 +319,10 @@ export function parseValueDestructure(
               ) {
               ) {
                 isDynamic = true
                 isDynamic = true
                 helper = context.helper('getDefaultValue')
                 helper = context.helper('getDefaultValue')
-                helperArgs = rawValue.slice(
+                helperArgs = `() => (${rawValue.slice(
                   child.right.start! - 1,
                   child.right.start! - 1,
                   child.right.end! - 1,
                   child.right.end! - 1,
-                )
+                )})`
               }
               }
             }
             }
             map.set(id.name, { path, dynamic: isDynamic, helper, helperArgs })
             map.set(id.name, { path, dynamic: isDynamic, helper, helperArgs })

+ 4 - 1
packages/runtime-vapor/__tests__/for.spec.ts

@@ -543,6 +543,7 @@ describe('createFor', () => {
 
 
   test('de-structured value (default value)', async () => {
   test('de-structured value (default value)', async () => {
     const list = ref<any[]>([{ name: '1' }, { name: '2' }, { name: '3' }])
     const list = ref<any[]>([{ name: '1' }, { name: '2' }, { name: '3' }])
+    const getDefault = vi.fn(() => '0')
 
 
     const { host } = define(() => {
     const { host } = define(() => {
       const n1 = createFor(
       const n1 = createFor(
@@ -550,7 +551,7 @@ describe('createFor', () => {
         (item, _key, index) => {
         (item, _key, index) => {
           const span = document.createElement('li')
           const span = document.createElement('li')
           renderEffect(() => {
           renderEffect(() => {
-            span.innerHTML = getDefaultValue(item.value.x, '0')
+            span.innerHTML = getDefaultValue(item.value.x, getDefault)
             // index should be undefined if source is not an object
             // index should be undefined if source is not an object
             expect(index.value).toBe(undefined)
             expect(index.value).toBe(undefined)
           })
           })
@@ -562,11 +563,13 @@ describe('createFor', () => {
     }).render()
     }).render()
 
 
     expect(host.innerHTML).toBe('<li>0</li><li>0</li><li>0</li><!--for-->')
     expect(host.innerHTML).toBe('<li>0</li><li>0</li><li>0</li><!--for-->')
+    expect(getDefault).toHaveBeenCalledTimes(3)
 
 
     // change
     // change
     list.value[0].x = 5
     list.value[0].x = 5
     await nextTick()
     await nextTick()
     expect(host.innerHTML).toBe('<li>5</li><li>0</li><li>0</li><!--for-->')
     expect(host.innerHTML).toBe('<li>5</li><li>0</li><li>0</li><!--for-->')
+    expect(getDefault).toHaveBeenCalledTimes(3)
 
 
     // clear
     // clear
     list.value = []
     list.value = []

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

@@ -848,8 +848,8 @@ export function getRestElement(val: any, keys: string[]): any {
   return res
   return res
 }
 }
 
 
-export function getDefaultValue(val: any, defaultVal: any): any {
-  return val === undefined ? defaultVal : val
+export function getDefaultValue(val: any, getDefaultVal: () => any): any {
+  return val === undefined ? getDefaultVal() : val
 }
 }
 
 
 export function isForBlock(block: Block): block is ForBlock {
 export function isForBlock(block: Block): block is ForBlock {