Skip to content

Commit 4af8e87

Browse files
sxzzwangyewei
andcommitted
feat: support more source types for v-for
closes #139 Co-authored-by: wangjie36 <[email protected]>
1 parent d51d558 commit 4af8e87

File tree

7 files changed

+291
-80
lines changed

7 files changed

+291
-80
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ describe('compiler: v-for', () => {
5454
children: [{ template: 0 }],
5555
},
5656
},
57-
keyProperty: {
57+
keyProp: {
5858
type: NodeTypes.SIMPLE_EXPRESSION,
5959
content: 'item.id',
6060
},

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

+14-10
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,19 @@ import {
1111
NEWLINE,
1212
buildCodeFragment,
1313
genCall,
14+
genMulti,
1415
} from './utils'
1516

1617
export function genFor(
1718
oper: ForIRNode,
1819
context: CodegenContext,
1920
): CodeFragment[] {
2021
const { vaporHelper } = context
21-
const { source, value, key, render, keyProperty } = oper
22+
const { source, value, key, index, render, keyProp } = oper
2223

2324
const rawValue = value && value.content
2425
const rawKey = key && key.content
26+
const rawIndex = index && index.content
2527

2628
const sourceExpr = ['() => (', ...genExpression(source, context), ')']
2729
let updateFn = '_updateEffect'
@@ -30,6 +32,7 @@ export function genFor(
3032
const idMap: Record<string, string> = {}
3133
if (rawValue) idMap[rawValue] = `_block.s[0]`
3234
if (rawKey) idMap[rawKey] = `_block.s[1]`
35+
if (rawIndex) idMap[rawIndex] = `_block.s[2]`
3336

3437
const blockReturns = (returns: CodeFragment[]): CodeFragment[] => [
3538
'[',
@@ -43,19 +46,20 @@ export function genFor(
4346
)
4447

4548
let getKeyFn: CodeFragment[] | false = false
46-
if (keyProperty) {
49+
if (keyProp) {
4750
const idMap: Record<string, null> = {}
4851
if (rawValue) idMap[rawValue] = null
4952
if (rawKey) idMap[rawKey] = null
50-
const expr = context.withId(
51-
() => genExpression(keyProperty, context),
52-
idMap,
53-
)
53+
if (rawIndex) idMap[rawIndex] = null
54+
const expr = context.withId(() => genExpression(keyProp, context), idMap)
5455
getKeyFn = [
55-
'(',
56-
rawValue ? rawValue : rawKey ? '_' : '',
57-
rawKey && `, ${rawKey}`,
58-
') => (',
56+
...genMulti(
57+
['(', ')', ', '],
58+
rawValue ? rawValue : rawKey || rawIndex ? '_' : undefined,
59+
rawKey ? rawKey : rawIndex ? '__' : undefined,
60+
rawIndex,
61+
),
62+
' => (',
5963
...expr,
6064
')',
6165
]

packages/compiler-vapor/src/ir.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export interface ForIRNode extends BaseIRNode {
7373
value?: SimpleExpressionNode
7474
key?: SimpleExpressionNode
7575
index?: SimpleExpressionNode
76-
keyProperty?: SimpleExpressionNode
76+
keyProp?: SimpleExpressionNode
7777
render: BlockIRNode
7878
}
7979

packages/compiler-vapor/src/transforms/vFor.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export function processFor(
7373
value: value as SimpleExpressionNode | undefined,
7474
key: key as SimpleExpressionNode | undefined,
7575
index: index as SimpleExpressionNode | undefined,
76-
keyProperty,
76+
keyProp: keyProperty,
7777
render,
7878
})
7979
}
+151-27
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,187 @@
1-
import {
2-
computed,
3-
createFor,
4-
createTextNode,
5-
nextTick,
6-
ref,
7-
renderEffect,
8-
setText,
9-
} from '../src'
1+
import { createFor, nextTick, ref, renderEffect } from '../src'
102
import { makeRender } from './_utils'
113

124
const define = makeRender()
135

146
describe('createFor', () => {
15-
test('basic', async () => {
7+
test('array source', async () => {
168
const list = ref([{ name: '1' }, { name: '2' }, { name: '3' }])
17-
const sort = ref(false)
18-
const sortedList = computed(() =>
19-
sort.value ? Array.from(list.value).reverse() : list.value,
20-
)
9+
function reverse() {
10+
list.value = list.value.reverse()
11+
}
2112

2213
const { host } = define(() => {
2314
const n1 = createFor(
24-
() => sortedList.value,
15+
() => list.value,
2516
block => {
26-
const n3 = createTextNode()
17+
const span = document.createElement('li')
2718
const update = () => {
28-
const [item] = block.s
29-
setText(n3, item.name)
19+
const [item, key, index] = block.s
20+
span.innerHTML = `${key}. ${item.name}`
21+
22+
// index should be undefined if source is not an object
23+
expect(index).toBe(undefined)
3024
}
3125
renderEffect(update)
32-
return [n3, update]
26+
return [span, update]
3327
},
28+
item => item.name,
3429
)
35-
return [n1]
30+
return n1
3631
}).render()
3732

38-
expect(host.innerHTML).toBe('123<!--for-->')
33+
expect(host.innerHTML).toBe(
34+
'<li>0. 1</li><li>1. 2</li><li>2. 3</li><!--for-->',
35+
)
3936

4037
// add
4138
list.value.push({ name: '4' })
4239
await nextTick()
43-
expect(host.innerHTML).toBe('1234<!--for-->')
40+
expect(host.innerHTML).toBe(
41+
'<li>0. 1</li><li>1. 2</li><li>2. 3</li><li>3. 4</li><!--for-->',
42+
)
4443

4544
// move
46-
sort.value = true
45+
reverse()
4746
await nextTick()
48-
expect(host.innerHTML).toBe('4321<!--for-->')
49-
sort.value = false
47+
expect(host.innerHTML).toBe(
48+
'<li>0. 4</li><li>1. 3</li><li>2. 2</li><li>3. 1</li><!--for-->',
49+
)
50+
51+
reverse()
5052
await nextTick()
51-
expect(host.innerHTML).toBe('1234<!--for-->')
53+
expect(host.innerHTML).toBe(
54+
'<li>0. 1</li><li>1. 2</li><li>2. 3</li><li>3. 4</li><!--for-->',
55+
)
5256

5357
// change
5458
list.value[0].name = 'a'
5559
await nextTick()
56-
expect(host.innerHTML).toBe('a234<!--for-->')
60+
expect(host.innerHTML).toBe(
61+
'<li>0. a</li><li>1. 2</li><li>2. 3</li><li>3. 4</li><!--for-->',
62+
)
5763

5864
// remove
65+
list.value.splice(1, 1)
66+
await nextTick()
67+
expect(host.innerHTML).toBe(
68+
'<li>0. a</li><li>1. 3</li><li>2. 4</li><!--for-->',
69+
)
70+
71+
// clear
5972
list.value = []
6073
await nextTick()
6174
expect(host.innerHTML).toBe('<!--for-->')
6275
})
76+
77+
test('number source', async () => {
78+
const count = ref(3)
79+
80+
const { host } = define(() => {
81+
const n1 = createFor(
82+
() => count.value,
83+
block => {
84+
const span = document.createElement('li')
85+
const update = () => {
86+
const [item, key, index] = block.s
87+
span.innerHTML = `${key}. ${item}`
88+
89+
// index should be undefined if source is not an object
90+
expect(index).toBe(undefined)
91+
}
92+
renderEffect(update)
93+
return [span, update]
94+
},
95+
item => item.name,
96+
)
97+
return n1
98+
}).render()
99+
100+
expect(host.innerHTML).toBe(
101+
'<li>0. 1</li><li>1. 2</li><li>2. 3</li><!--for-->',
102+
)
103+
104+
// add
105+
count.value = 4
106+
await nextTick()
107+
expect(host.innerHTML).toBe(
108+
'<li>0. 1</li><li>1. 2</li><li>2. 3</li><li>3. 4</li><!--for-->',
109+
)
110+
111+
// remove
112+
count.value = 2
113+
await nextTick()
114+
expect(host.innerHTML).toBe('<li>0. 1</li><li>1. 2</li><!--for-->')
115+
116+
// clear
117+
count.value = 0
118+
await nextTick()
119+
expect(host.innerHTML).toBe('<!--for-->')
120+
})
121+
122+
test('object source', async () => {
123+
const initial = () => ({ a: 1, b: 2, c: 3 })
124+
const data = ref<Record<string, number>>(initial())
125+
126+
const { host } = define(() => {
127+
const n1 = createFor(
128+
() => data.value,
129+
block => {
130+
const span = document.createElement('li')
131+
const update = () => {
132+
const [item, key, index] = block.s
133+
span.innerHTML = `${key}${index}. ${item}`
134+
expect(index).not.toBe(undefined)
135+
}
136+
renderEffect(update)
137+
return [span, update]
138+
},
139+
item => {
140+
return item
141+
},
142+
)
143+
return n1
144+
}).render()
145+
146+
expect(host.innerHTML).toBe(
147+
'<li>a0. 1</li><li>b1. 2</li><li>c2. 3</li><!--for-->',
148+
)
149+
150+
// move
151+
data.value = {
152+
c: 3,
153+
b: 2,
154+
a: 1,
155+
}
156+
await nextTick()
157+
expect(host.innerHTML).toBe(
158+
'<li>c0. 3</li><li>b1. 2</li><li>a2. 1</li><!--for-->',
159+
)
160+
161+
// add
162+
data.value.d = 4
163+
await nextTick()
164+
expect(host.innerHTML).toBe(
165+
'<li>c0. 3</li><li>b1. 2</li><li>a2. 1</li><li>d3. 4</li><!--for-->',
166+
)
167+
168+
// change
169+
data.value.b = 100
170+
await nextTick()
171+
expect(host.innerHTML).toBe(
172+
'<li>c0. 3</li><li>b1. 100</li><li>a2. 1</li><li>d3. 4</li><!--for-->',
173+
)
174+
175+
// remove
176+
delete data.value.c
177+
await nextTick()
178+
expect(host.innerHTML).toBe(
179+
'<li>b0. 100</li><li>a1. 1</li><li>d2. 4</li><!--for-->',
180+
)
181+
182+
// clear
183+
data.value = {}
184+
await nextTick()
185+
expect(host.innerHTML).toBe('<!--for-->')
186+
})
63187
})

0 commit comments

Comments
 (0)