7
7
import { BaseComponent , getDomSibling } from '../component' ;
8
8
import { Fragment } from '../create-element' ;
9
9
import { diffChildren } from './children' ;
10
- import { diffProps , setProperty } from './props' ;
10
+ import { setProperty } from './props' ;
11
11
import { assign , isArray , removeNode , slice } from '../util' ;
12
12
import options from '../options' ;
13
13
@@ -364,24 +364,33 @@ function diffElementNodes(
364
364
let newProps = newVNode . props ;
365
365
let nodeType = /** @type {string } */ ( newVNode . type ) ;
366
366
/** @type {any } */
367
- let i = 0 ;
367
+ let i ;
368
+ /** @type {{ __html?: string } } */
369
+ let newHtml ;
370
+ /** @type {{ __html?: string } } */
371
+ let oldHtml ;
372
+ /** @type {ComponentChildren } */
373
+ let newChildren ;
374
+ let value ;
375
+ let inputValue ;
376
+ let checked ;
368
377
369
378
// Tracks entering and exiting SVG namespace when descending through the tree.
370
379
if ( nodeType === 'svg' ) isSvg = true ;
371
380
372
381
if ( excessDomChildren != null ) {
373
- for ( ; i < excessDomChildren . length ; i ++ ) {
374
- const child = excessDomChildren [ i ] ;
382
+ for ( i = 0 ; i < excessDomChildren . length ; i ++ ) {
383
+ value = excessDomChildren [ i ] ;
375
384
376
385
// if newVNode matches an element in excessDomChildren or the `dom`
377
386
// argument matches an element in excessDomChildren, remove it from
378
387
// excessDomChildren so it isn't later removed in diffChildren
379
388
if (
380
- child &&
381
- 'setAttribute' in child === ! ! nodeType &&
382
- ( nodeType ? child . localName === nodeType : child . nodeType === 3 )
389
+ value &&
390
+ 'setAttribute' in value === ! ! nodeType &&
391
+ ( nodeType ? value . localName === nodeType : value . nodeType === 3 )
383
392
) {
384
- dom = child ;
393
+ dom = value ;
385
394
excessDomChildren [ i ] = null ;
386
395
break ;
387
396
}
@@ -401,7 +410,8 @@ function diffElementNodes(
401
410
402
411
// we created a new parent, so none of the previously attached children can be reused:
403
412
excessDomChildren = null ;
404
- // we are creating a new node, so we can assume this is a new subtree (in case we are hydrating), this deopts the hydrate
413
+ // we are creating a new node, so we can assume this is a new subtree (in
414
+ // case we are hydrating), this deopts the hydrate
405
415
isHydrating = false ;
406
416
}
407
417
@@ -416,43 +426,67 @@ function diffElementNodes(
416
426
417
427
oldProps = oldVNode . props || EMPTY_OBJ ;
418
428
419
- let oldHtml = oldProps . dangerouslySetInnerHTML ;
420
- let newHtml = newProps . dangerouslySetInnerHTML ;
421
-
422
- // During hydration, props are not diffed at all (including dangerouslySetInnerHTML)
423
- // @TODO we should warn in debug mode when props don't match here.
424
- if ( ! isHydrating ) {
425
- // But, if we are in a situation where we are using existing DOM (e.g. replaceNode)
426
- // we should read the existing DOM attributes to diff them
427
- if ( excessDomChildren != null ) {
428
- oldProps = { } ;
429
- for ( i = 0 ; i < dom . attributes . length ; i ++ ) {
430
- oldProps [ dom . attributes [ i ] . name ] = dom . attributes [ i ] . value ;
431
- }
429
+ // If we are in a situation where we are not hydrating but are using
430
+ // existing DOM (e.g. replaceNode) we should read the existing DOM
431
+ // attributes to diff them
432
+ if ( ! isHydrating && excessDomChildren != null ) {
433
+ oldProps = { } ;
434
+ for ( i = 0 ; i < dom . attributes . length ; i ++ ) {
435
+ value = dom . attributes [ i ] ;
436
+ oldProps [ value . name ] = value . value ;
432
437
}
438
+ }
433
439
434
- if ( newHtml || oldHtml ) {
435
- // Avoid re-applying the same '__html' if it did not changed between re-render
436
- if (
437
- ! newHtml ||
438
- ( ( ! oldHtml || newHtml . __html != oldHtml . __html ) &&
439
- newHtml . __html !== dom . innerHTML )
440
- ) {
441
- dom . innerHTML = ( newHtml && newHtml . __html ) || '' ;
442
- }
440
+ for ( i in oldProps ) {
441
+ value = oldProps [ i ] ;
442
+ if ( i == 'children' ) {
443
+ } else if ( i == 'dangerouslySetInnerHTML' ) {
444
+ oldHtml = value ;
445
+ } else if ( i !== 'key' && ! ( i in newProps ) ) {
446
+ setProperty ( dom , i , null , value , isSvg ) ;
443
447
}
444
448
}
445
449
446
- diffProps ( dom , newProps , oldProps , isSvg , isHydrating ) ;
450
+ // During hydration, props are not diffed at all (including dangerouslySetInnerHTML)
451
+ // @TODO we should warn in debug mode when props don't match here.
452
+ for ( i in newProps ) {
453
+ value = newProps [ i ] ;
454
+ if ( i == 'children' ) {
455
+ newChildren = value ;
456
+ } else if ( i == 'dangerouslySetInnerHTML' ) {
457
+ newHtml = value ;
458
+ } else if ( i == 'value' ) {
459
+ inputValue = value ;
460
+ } else if ( i == 'checked' ) {
461
+ checked = value ;
462
+ } else if (
463
+ i !== 'key' &&
464
+ ( ! isHydrating || typeof value == 'function' ) &&
465
+ oldProps [ i ] !== value
466
+ ) {
467
+ setProperty ( dom , i , value , oldProps [ i ] , isSvg ) ;
468
+ }
469
+ }
447
470
448
471
// If the new vnode didn't have dangerouslySetInnerHTML, diff its children
449
472
if ( newHtml ) {
473
+ // Avoid re-applying the same '__html' if it did not changed between re-render
474
+ if (
475
+ ! isHydrating &&
476
+ ( ! oldHtml ||
477
+ ( newHtml . __html !== oldHtml . __html &&
478
+ newHtml . __html !== dom . innerHTML ) )
479
+ ) {
480
+ dom . innerHTML = newHtml . __html ;
481
+ }
482
+
450
483
newVNode . _children = [ ] ;
451
484
} else {
452
- i = newVNode . props . children ;
485
+ if ( oldHtml ) dom . innerHTML = '' ;
486
+
453
487
diffChildren (
454
488
dom ,
455
- isArray ( i ) ? i : [ i ] ,
489
+ isArray ( newChildren ) ? newChildren : [ newChildren ] ,
456
490
newVNode ,
457
491
oldVNode ,
458
492
globalContext ,
@@ -474,30 +508,28 @@ function diffElementNodes(
474
508
}
475
509
}
476
510
477
- // (as above, don't diff props during hydration)
511
+ // As above, don't diff props during hydration
478
512
if ( ! isHydrating ) {
513
+ i = 'value' ;
479
514
if (
480
- 'value' in newProps &&
481
- ( i = newProps . value ) !== undefined &&
515
+ inputValue !== undefined &&
482
516
// #2756 For the <progress>-element the initial value is 0,
483
517
// despite the attribute not being present. When the attribute
484
518
// is missing the progress bar is treated as indeterminate.
485
519
// To fix that we'll always update it when it is 0 for progress elements
486
- ( i !== dom . value ||
487
- ( nodeType === 'progress' && ! i ) ||
520
+ ( inputValue !== dom [ i ] ||
521
+ ( nodeType === 'progress' && ! inputValue ) ||
488
522
// This is only for IE 11 to fix <select> value not being updated.
489
523
// To avoid a stale select value we need to set the option.value
490
524
// again, which triggers IE11 to re-evaluate the select value
491
- ( nodeType === 'option' && i !== oldProps . value ) )
525
+ ( nodeType === 'option' && inputValue !== oldProps [ i ] ) )
492
526
) {
493
- setProperty ( dom , 'value' , i , oldProps . value , false ) ;
527
+ setProperty ( dom , i , inputValue , oldProps [ i ] , false ) ;
494
528
}
495
- if (
496
- 'checked' in newProps &&
497
- ( i = newProps . checked ) !== undefined &&
498
- i !== dom . checked
499
- ) {
500
- setProperty ( dom , 'checked' , i , oldProps . checked , false ) ;
529
+
530
+ i = 'checked' ;
531
+ if ( checked !== undefined && checked !== dom [ i ] ) {
532
+ setProperty ( dom , i , checked , oldProps [ i ] , false ) ;
501
533
}
502
534
}
503
535
}
0 commit comments