Skip to content

Commit 868c429

Browse files
LittleSoundsxzz
andauthored
feat: destructuring + nesting in v-for (#217)
Co-authored-by: 三咲智子 Kevin Deng <[email protected]>
1 parent 00c6e6d commit 868c429

File tree

13 files changed

+612
-205
lines changed

13 files changed

+612
-205
lines changed

packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ exports[`compiler: template ref transform > ref + v-for 1`] = `
1717
const t0 = _template("<div></div>")
1818
1919
export function render(_ctx) {
20-
const n0 = _createFor(() => ([1,2,3]), (_block) => {
20+
const n0 = _createFor(() => ([1,2,3]), (_ctx0) => {
2121
const n2 = t0()
2222
_setRef(n2, "foo", void 0, true)
23-
return [n2, () => {}]
23+
return n2
2424
})
2525
return n0
2626
}"

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

Lines changed: 83 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,102 @@
11
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
22

3+
exports[`compiler: v-for > array de-structured value 1`] = `
4+
"import { renderEffect as _renderEffect, setText as _setText, createFor as _createFor, template as _template } from 'vue/vapor';
5+
const t0 = _template("<div></div>")
6+
7+
export function render(_ctx) {
8+
const n0 = _createFor(() => (_ctx.list), (_ctx0) => {
9+
const n2 = t0()
10+
_renderEffect(() => _setText(n2, _ctx0[0] + _ctx0[1] + _ctx0[2]))
11+
return n2
12+
}, ([id, ...other], index) => (id), null, null, false, _state => {
13+
const [[id, ...other], index] = _state
14+
return [id, other, index]
15+
})
16+
return n0
17+
}"
18+
`;
19+
320
exports[`compiler: v-for > basic v-for 1`] = `
4-
"import { delegate as _delegate, setText as _setText, renderEffect as _renderEffect, createFor as _createFor, delegateEvents as _delegateEvents, template as _template } from 'vue/vapor';
21+
"import { delegate as _delegate, renderEffect as _renderEffect, setText as _setText, createFor as _createFor, delegateEvents as _delegateEvents, template as _template } from 'vue/vapor';
522
const t0 = _template("<div></div>")
623
_delegateEvents("click")
724
825
export function render(_ctx) {
9-
const n0 = _createFor(() => (_ctx.items), (_block) => {
26+
const n0 = _createFor(() => (_ctx.items), (_ctx0) => {
1027
const n2 = t0()
11-
_delegate(n2, "click", () => $event => (_ctx.remove(_block.s[0])))
12-
const _updateEffect = () => {
13-
const [item] = _block.s
14-
_setText(n2, item)
15-
}
16-
_renderEffect(_updateEffect)
17-
return [n2, _updateEffect]
28+
_delegate(n2, "click", () => $event => (_ctx.remove(_ctx0[0])))
29+
_renderEffect(() => _setText(n2, _ctx0[0]))
30+
return n2
1831
}, (item) => (item.id))
1932
return n0
2033
}"
2134
`;
2235

2336
exports[`compiler: v-for > multi effect 1`] = `
24-
"import { setDynamicProp as _setDynamicProp, renderEffect as _renderEffect, createFor as _createFor, template as _template } from 'vue/vapor';
37+
"import { renderEffect as _renderEffect, setDynamicProp as _setDynamicProp, createFor as _createFor, template as _template } from 'vue/vapor';
38+
const t0 = _template("<div></div>")
39+
40+
export function render(_ctx) {
41+
const n0 = _createFor(() => (_ctx.items), (_ctx0) => {
42+
const n2 = t0()
43+
_renderEffect(() => _setDynamicProp(n2, "item", _ctx0[0]))
44+
_renderEffect(() => _setDynamicProp(n2, "index", _ctx0[1]))
45+
return n2
46+
})
47+
return n0
48+
}"
49+
`;
50+
51+
exports[`compiler: v-for > nested v-for 1`] = `
52+
"import { renderEffect as _renderEffect, setText as _setText, createFor as _createFor, insert as _insert, template as _template } from 'vue/vapor';
53+
const t0 = _template("<span></span>")
54+
const t1 = _template("<div></div>")
55+
56+
export function render(_ctx) {
57+
const n0 = _createFor(() => (_ctx.list), (_ctx0) => {
58+
const n5 = t1()
59+
const n2 = _createFor(() => (_ctx0[0]), (_ctx2) => {
60+
const n4 = t0()
61+
_renderEffect(() => _setText(n4, _ctx2[0]+_ctx0[0]))
62+
return n4
63+
})
64+
_insert(n2, n5)
65+
return n5
66+
})
67+
return n0
68+
}"
69+
`;
70+
71+
exports[`compiler: v-for > object de-structured value 1`] = `
72+
"import { renderEffect as _renderEffect, setText as _setText, createFor as _createFor, template as _template } from 'vue/vapor';
73+
const t0 = _template("<div></div>")
74+
75+
export function render(_ctx) {
76+
const n0 = _createFor(() => (_ctx.list), (_ctx0) => {
77+
const n2 = t0()
78+
_renderEffect(() => _setText(n2, _ctx0[0] + _ctx0[1] + _ctx0[2]))
79+
return n2
80+
}, ({ id, ...other }, index) => (id), null, null, false, _state => {
81+
const [{ id, ...other }, index] = _state
82+
return [id, other, index]
83+
})
84+
return n0
85+
}"
86+
`;
87+
88+
exports[`compiler: v-for > v-for aliases w/ complex expressions 1`] = `
89+
"import { renderEffect as _renderEffect, setText as _setText, createFor as _createFor, template as _template } from 'vue/vapor';
2590
const t0 = _template("<div></div>")
2691
2792
export function render(_ctx) {
28-
const n0 = _createFor(() => (_ctx.items), (_block) => {
93+
const n0 = _createFor(() => (_ctx.list), (_ctx0) => {
2994
const n2 = t0()
30-
const _updateEffect = () => {
31-
const [item, index] = _block.s
32-
_setDynamicProp(n2, "item", item)
33-
_setDynamicProp(n2, "index", index)
34-
}
35-
_renderEffect(_updateEffect)
36-
return [n2, _updateEffect]
95+
_renderEffect(() => _setText(n2, _ctx0[0] + _ctx.bar + _ctx.baz + _ctx0[1] + _ctx.quux))
96+
return n2
97+
}, null, null, null, false, _state => {
98+
const [{ foo = bar, baz: [qux = quux] }] = _state
99+
return [foo, qux]
37100
})
38101
return n0
39102
}"
@@ -44,9 +107,9 @@ exports[`compiler: v-for > w/o value 1`] = `
44107
const t0 = _template("<div>item</div>")
45108
46109
export function render(_ctx) {
47-
const n0 = _createFor(() => (_ctx.items), (_block) => {
110+
const n0 = _createFor(() => (_ctx.items), (_ctx0) => {
48111
const n2 = t0()
49-
return [n2, () => {}]
112+
return n2
50113
})
51114
return n0
52115
}"

packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,9 @@ exports[`compiler: v-once > with v-for 1`] = `
6767
const t0 = _template("<div></div>")
6868
6969
export function render(_ctx) {
70-
const n0 = _createFor(() => (_ctx.list), (_block) => {
70+
const n0 = _createFor(() => (_ctx.list), (_ctx0) => {
7171
const n2 = t0()
72-
return [n2, () => {}]
72+
return n2
7373
}, null, null, null, true)
7474
return n0
7575
}"

packages/compiler-vapor/__tests__/transforms/vFor.spec.ts

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,141 @@ describe('compiler: v-for', () => {
8686
)
8787
expect(code).matchSnapshot()
8888
})
89+
90+
test('nested v-for', () => {
91+
const { code, ir } = compileWithVFor(
92+
`<div v-for="i in list"><span v-for="j in i">{{ j+i }}</span></div>`,
93+
)
94+
expect(code).matchSnapshot()
95+
expect(code).contains(`_createFor(() => (_ctx.list), (_ctx0) => {`)
96+
expect(code).contains(`_createFor(() => (_ctx0[0]), (_ctx2) => {`)
97+
expect(code).contains(`_ctx2[0]+_ctx0[0]`)
98+
expect(ir.template).toEqual(['<span></span>', '<div></div>'])
99+
expect(ir.block.operation).toMatchObject([
100+
{
101+
type: IRNodeTypes.FOR,
102+
id: 0,
103+
source: { content: 'list' },
104+
value: { content: 'i' },
105+
render: {
106+
type: IRNodeTypes.BLOCK,
107+
dynamic: {
108+
children: [{ template: 1 }],
109+
},
110+
},
111+
},
112+
])
113+
expect((ir.block.operation[0] as any).render.operation[0]).toMatchObject({
114+
type: IRNodeTypes.FOR,
115+
id: 2,
116+
source: { content: 'i' },
117+
value: { content: 'j' },
118+
render: {
119+
type: IRNodeTypes.BLOCK,
120+
dynamic: {
121+
children: [{ template: 0 }],
122+
},
123+
},
124+
})
125+
})
126+
127+
test('object de-structured value', () => {
128+
const { code, ir } = compileWithVFor(
129+
`<div v-for="( { id, ...other }, index) in list" :key="id">{{ id + other + index }}</div>`,
130+
)
131+
expect(code).matchSnapshot()
132+
expect(code).contains(`return [id, other, index]`)
133+
expect(code).contains(`_ctx0[0] + _ctx0[1] + _ctx0[2]`)
134+
expect(ir.block.operation[0]).toMatchObject({
135+
type: IRNodeTypes.FOR,
136+
source: {
137+
type: NodeTypes.SIMPLE_EXPRESSION,
138+
content: 'list',
139+
},
140+
value: {
141+
type: NodeTypes.SIMPLE_EXPRESSION,
142+
content: '{ id, ...other }',
143+
ast: {
144+
type: 'ArrowFunctionExpression',
145+
params: [
146+
{
147+
type: 'ObjectPattern',
148+
},
149+
],
150+
},
151+
},
152+
key: {
153+
type: NodeTypes.SIMPLE_EXPRESSION,
154+
content: 'index',
155+
},
156+
index: undefined,
157+
})
158+
})
159+
160+
test('array de-structured value', () => {
161+
const { code, ir } = compileWithVFor(
162+
`<div v-for="([id, ...other], index) in list" :key="id">{{ id + other + index }}</div>`,
163+
)
164+
expect(code).matchSnapshot()
165+
expect(code).contains(`return [id, other, index]`)
166+
expect(code).contains(`_ctx0[0] + _ctx0[1] + _ctx0[2]`)
167+
expect(ir.block.operation[0]).toMatchObject({
168+
type: IRNodeTypes.FOR,
169+
source: {
170+
type: NodeTypes.SIMPLE_EXPRESSION,
171+
content: 'list',
172+
},
173+
value: {
174+
type: NodeTypes.SIMPLE_EXPRESSION,
175+
content: '[id, ...other]',
176+
ast: {
177+
type: 'ArrowFunctionExpression',
178+
params: [
179+
{
180+
type: 'ArrayPattern',
181+
},
182+
],
183+
},
184+
},
185+
key: {
186+
type: NodeTypes.SIMPLE_EXPRESSION,
187+
content: 'index',
188+
},
189+
index: undefined,
190+
})
191+
})
192+
193+
test('v-for aliases w/ complex expressions', () => {
194+
const { code, ir } = compileWithVFor(
195+
`<div v-for="({ foo = bar, baz: [qux = quux] }) in list">
196+
{{ foo + bar + baz + qux + quux }}
197+
</div>`,
198+
)
199+
expect(code).matchSnapshot()
200+
expect(code).contains(`return [foo, qux]`)
201+
expect(code).contains(
202+
`_ctx0[0] + _ctx.bar + _ctx.baz + _ctx0[1] + _ctx.quux`,
203+
)
204+
expect(ir.block.operation[0]).toMatchObject({
205+
type: IRNodeTypes.FOR,
206+
source: {
207+
type: NodeTypes.SIMPLE_EXPRESSION,
208+
content: 'list',
209+
},
210+
value: {
211+
type: NodeTypes.SIMPLE_EXPRESSION,
212+
content: '{ foo = bar, baz: [qux = quux] }',
213+
ast: {
214+
type: 'ArrowFunctionExpression',
215+
params: [
216+
{
217+
type: 'ObjectPattern',
218+
},
219+
],
220+
},
221+
},
222+
key: undefined,
223+
index: undefined,
224+
})
225+
})
89226
})

packages/compiler-vapor/src/generate.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type {
22
CodegenOptions as BaseCodegenOptions,
33
BaseCodegenResult,
44
} from '@vue/compiler-dom'
5-
import type { BlockIRNode, IREffect, RootIRNode, VaporHelper } from './ir'
5+
import type { BlockIRNode, RootIRNode, VaporHelper } from './ir'
66
import { extend, remove } from '@vue/shared'
77
import { genBlockContent } from './generators/block'
88
import { genTemplates } from './generators/template'
@@ -38,10 +38,6 @@ export class CodegenContext {
3838
identifiers: Record<string, string[]> = Object.create(null)
3939

4040
block: BlockIRNode
41-
genEffects: Array<
42-
(effects: IREffect[], context: CodegenContext) => CodeFragment[]
43-
> = []
44-
4541
withId<T>(fn: () => T, map: Record<string, string | null>): T {
4642
const { identifiers } = this
4743
const ids = Object.keys(map)

packages/compiler-vapor/src/generators/block.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,7 @@ export function genBlockContent(
5353
}
5454

5555
push(...genOperations(operation, context))
56-
push(
57-
...(context.genEffects.length
58-
? context.genEffects[context.genEffects.length - 1]
59-
: genEffects)(effect, context),
60-
)
56+
push(...genEffects(effect, context))
6157

6258
push(NEWLINE, `return `)
6359

0 commit comments

Comments
 (0)