1
- import { EffectScope , type ShallowRef , shallowRef } from '@vue/reactivity'
1
+ import {
2
+ EffectScope ,
3
+ type ShallowRef ,
4
+ isReactive ,
5
+ isShallow ,
6
+ shallowReadArray ,
7
+ shallowRef ,
8
+ toReactive ,
9
+ } from '@vue/reactivity'
2
10
import { getSequence , isArray , isObject , isString } from '@vue/shared'
3
11
import { createComment , createTextNode } from './dom/node'
4
12
import { type Block , Fragment , insert , remove as removeBlock } from './block'
@@ -33,6 +41,12 @@ class ForBlock extends Fragment {
33
41
34
42
type Source = any [ ] | Record < any , any > | number | Set < any > | Map < any , any >
35
43
44
+ type ResolvedSource = {
45
+ values : any [ ]
46
+ needsWrap : boolean
47
+ keys ?: string [ ]
48
+ }
49
+
36
50
/*! #__NO_SIDE_EFFECTS__ */
37
51
export const createFor = (
38
52
src : ( ) => Source ,
@@ -59,8 +73,8 @@ export const createFor = (
59
73
}
60
74
61
75
const renderList = ( ) => {
62
- const source = src ( )
63
- const newLength = getLength ( source )
76
+ const source = normalizeSource ( src ( ) )
77
+ const newLength = source . values . length
64
78
const oldLength = oldBlocks . length
65
79
newBlocks = new Array ( newLength )
66
80
@@ -85,8 +99,7 @@ export const createFor = (
85
99
// unkeyed fast path
86
100
const commonLength = Math . min ( newLength , oldLength )
87
101
for ( let i = 0 ; i < commonLength ; i ++ ) {
88
- const [ item ] = getItem ( source , i )
89
- update ( ( newBlocks [ i ] = oldBlocks [ i ] ) , item )
102
+ update ( ( newBlocks [ i ] = oldBlocks [ i ] ) , getItem ( source , i ) [ 0 ] )
90
103
}
91
104
for ( let i = oldLength ; i < newLength ; i ++ ) {
92
105
mount ( source , i )
@@ -249,7 +262,7 @@ export const createFor = (
249
262
}
250
263
251
264
const mount = (
252
- source : any ,
265
+ source : ResolvedSource ,
253
266
idx : number ,
254
267
anchor : Node | undefined = parentAnchor ,
255
268
) : ForBlock => {
@@ -319,54 +332,59 @@ export const createFor = (
319
332
}
320
333
321
334
export function createForSlots (
322
- source : Source ,
335
+ rawSource : Source ,
323
336
getSlot : ( item : any , key : any , index ?: number ) => DynamicSlot ,
324
337
) : DynamicSlot [ ] {
325
- const sourceLength = getLength ( source )
338
+ const source = normalizeSource ( rawSource )
339
+ const sourceLength = source . values . length
326
340
const slots = new Array < DynamicSlot > ( sourceLength )
327
341
for ( let i = 0 ; i < sourceLength ; i ++ ) {
328
- const [ item , key , index ] = getItem ( source , i )
329
- slots [ i ] = getSlot ( item , key , index )
342
+ slots [ i ] = getSlot ( ...getItem ( source , i ) )
330
343
}
331
344
return slots
332
345
}
333
346
334
- function getLength ( source : any ) : number {
335
- if ( isArray ( source ) || isString ( source ) ) {
336
- return source . length
347
+ function normalizeSource ( source : any ) : ResolvedSource {
348
+ let values = source
349
+ let needsWrap = false
350
+ let keys
351
+ if ( isArray ( source ) ) {
352
+ if ( isReactive ( source ) ) {
353
+ needsWrap = ! isShallow ( source )
354
+ values = shallowReadArray ( source )
355
+ }
356
+ } else if ( isString ( source ) ) {
357
+ values = source . split ( '' )
337
358
} else if ( typeof source === 'number' ) {
338
359
if ( __DEV__ && ! Number . isInteger ( source ) ) {
339
360
warn ( `The v-for range expect an integer value but got ${ source } .` )
340
361
}
341
- return source
362
+ values = new Array ( source )
363
+ for ( let i = 0 ; i < source ; i ++ ) values [ i ] = i + 1
342
364
} else if ( isObject ( source ) ) {
343
365
if ( source [ Symbol . iterator as any ] ) {
344
- return Array . from ( source as Iterable < any > ) . length
366
+ values = Array . from ( source as Iterable < any > )
345
367
} else {
346
- return Object . keys ( source ) . length
368
+ keys = Object . keys ( source )
369
+ values = new Array ( keys . length )
370
+ for ( let i = 0 , l = keys . length ; i < l ; i ++ ) {
371
+ values [ i ] = source [ keys [ i ] ]
372
+ }
347
373
}
348
374
}
349
- return 0
375
+ return { values , needsWrap , keys }
350
376
}
351
377
352
378
function getItem (
353
- source : any ,
379
+ { keys , values , needsWrap } : ResolvedSource ,
354
380
idx : number ,
355
381
) : [ item : any , key : any , index ?: number ] {
356
- if ( isArray ( source ) || isString ( source ) ) {
357
- return [ source [ idx ] , idx , undefined ]
358
- } else if ( typeof source === 'number' ) {
359
- return [ idx + 1 , idx , undefined ]
360
- } else if ( isObject ( source ) ) {
361
- if ( source [ Symbol . iterator as any ] ) {
362
- source = Array . from ( source as Iterable < any > )
363
- return [ source [ idx ] , idx , undefined ]
364
- } else {
365
- const key = Object . keys ( source ) [ idx ]
366
- return [ source [ key ] , key , idx ]
367
- }
382
+ const value = needsWrap ? toReactive ( values [ idx ] ) : values [ idx ]
383
+ if ( keys ) {
384
+ return [ value , keys [ idx ] , idx ]
385
+ } else {
386
+ return [ value , idx , undefined ]
368
387
}
369
- return null !
370
388
}
371
389
372
390
function normalizeAnchor ( node : Block ) : Node {
0 commit comments