192
192
}
193
193
const mountStatefulComponent = function ( vnode , container , isSVG ) {
194
194
// 创建组件实例
195
- const instance = new vnode . tag ( )
196
- // 渲染vnode
197
- instance . $vnode = instance . render ( h )
198
- // 挂载
199
- mount ( instance . $vnode , container , isSVG )
200
- // el 属性值 和 组件实例的 $el 属性都引用组件的根DOM元素
201
- instance . $el = vnode . el = instance . $vnode . el
195
+ const instance = ( vnode . children = new vnode . tag ( ) )
196
+ instance . $props = vnode . data
197
+ instance . _update = function ( ) {
198
+ // 如果 instance._mounted 为真,说明组件已挂载,应该执行更新操作
199
+ if ( instance . _mounted ) {
200
+ // 1、拿到旧的 VNode
201
+ const prevVNode = instance . $vnode
202
+ // 2、重渲染新的 VNode
203
+ const nextVNode = ( instance . $vnode = instance . render ( ) )
204
+ // 3、patch 更新
205
+ patch ( prevVNode , nextVNode , prevVNode . el . parentNode )
206
+ // 4、更新 vnode.el 和 $el
207
+ instance . $el = vnode . el = instance . $vnode . el
208
+ } else {
209
+ // 1、渲染VNode
210
+ instance . $vnode = instance . render ( )
211
+ // 2、挂载
212
+ mount ( instance . $vnode , container , isSVG )
213
+ // 3、组件已挂载的标识
214
+ instance . _mounted = true
215
+ // 4、el 属性值 和 组件实例的 $el 属性都引用组件的根DOM元素
216
+ instance . $el = vnode . el = instance . $vnode . el
217
+ // 5、调用 mounted 钩子
218
+ instance . mounted && instance . mounted ( )
219
+ }
220
+ }
221
+
222
+ instance . _update ( )
223
+
202
224
}
203
225
204
226
const mountFunctionalComponent = function ( vnode , container , isSVG ) {
205
- const $vnode = vnode . tag ( )
206
- mount ( $vnode , container , isSVG )
207
- vnode . el = $vnode . el
227
+ // 获取 props
228
+ const props = vnode . data
229
+ // 获取 VNode
230
+ const $vnode = ( vnode . children = vnode . tag ( props ) )
231
+ // 挂载
232
+ mount ( $vnode , container , isSVG )
233
+ // el 元素引用该组件的根元素
234
+ vnode . el = $vnode . el
208
235
}
209
236
210
237
const mountComponent = function ( vnode , container , isSVG ) {
283
310
284
311
const replaceVNode = function ( prevVNode , nextVNode , container ) {
285
312
container . removeChild ( prevVNode . el )
313
+ // 如果将要被移除的 VNode 类型是组件,则需要调用该组件实例的 unmounted 钩子函数
314
+ if ( prevVNode . flags & VNodeFlags . COMPONENT_STATEFUL_NORMAL ) {
315
+ // 类型为有状态组件的 VNode,其 children 属性被用来存储组件实例对象
316
+ const instance = prevVNode . children
317
+ instance . unmounted && instance . unmounted ( )
318
+ }
286
319
mount ( nextVNode , container )
287
320
}
288
321
322
+ const patchData = function ( el , key , prevValue , nextValue , isSVG ) {
323
+ switch ( key ) {
324
+ case 'style' :
325
+ for ( let k in nextValue ) {
326
+ el . style [ k ] = nextValue [ k ]
327
+ }
328
+ for ( let k in prevValue ) {
329
+ if ( ! nextValue || ! nextValue . hasOwnProperty ( k ) ) {
330
+ el . style [ k ] = ''
331
+ }
332
+ }
333
+ break
334
+ case 'class' :
335
+ if ( nextValue ) {
336
+ if ( isSVG ) {
337
+ el . setAttribute ( 'class' , serialization ( nextValue ) )
338
+ } else {
339
+ el . className = serialization ( nextValue )
340
+ }
341
+ }
342
+ break
343
+ default :
344
+ const domPropsRE = / \[ A - Z ] | ^ (?: v a l u e | c h e c k e d | s e l e c t e d | m u t e d ) $ /
345
+ if ( key [ 0 ] === 'o' && key [ 1 ] === 'n' ) {
346
+ // 事件
347
+ // 移除旧事件
348
+ if ( prevValue ) {
349
+ el . removeEventListener ( key . slice ( 2 ) , prevValue )
350
+ }
351
+ // 添加新事件
352
+ if ( nextValue ) {
353
+ el . addEventListener ( key . slice ( 2 ) , nextValue )
354
+ }
355
+ } else if ( domPropsRE . test ( key ) ) {
356
+ // 当作 DOM Prop 处理
357
+ el [ key ] = nextValue
358
+ } else {
359
+ // 当作 Attr 处理
360
+ el . setAttribute ( key , nextValue )
361
+ }
362
+ break
363
+ }
364
+ }
365
+
366
+ // 3 * 3 = 9种情况
367
+ const patchChildren = function ( prevChildFlags , nextChildFlags , prevChildren , nextChildren , container ) {
368
+ switch ( prevChildFlags ) {
369
+ case ChildrenFlags . SINGLE_VNODE :
370
+ switch ( nextChildFlags ) {
371
+ case ChildrenFlags . SINGLE_VNODE :
372
+ patch ( prevChildren , nextChildren , container )
373
+ break
374
+ case ChildrenFlags . NO_CHILDREN :
375
+ container . removeChild ( prevChildren . el )
376
+ break
377
+ default :
378
+ container . removeChild ( prevChildren . el )
379
+ for ( let i = 0 ; i < nextChildren . length ; i ++ ) {
380
+ mount ( nextChildren [ i ] , container )
381
+ }
382
+ break
383
+ }
384
+ break
385
+ case ChildrenFlags . NO_CHILDREN :
386
+ switch ( nextChildFlags ) {
387
+ case ChildrenFlags . SINGLE_VNODE :
388
+ mount ( nextChildren , container )
389
+ break
390
+ case ChildrenFlags . NO_CHILDREN :
391
+ break
392
+ default :
393
+ for ( let i = 0 ; i < nextChildren . length ; i ++ ) {
394
+ mount ( nextChildren [ i ] , container )
395
+ }
396
+ break
397
+ }
398
+ break
399
+ default :
400
+ switch ( nextChildFlags ) {
401
+ case ChildrenFlags . SINGLE_VNODE :
402
+ for ( let i = 0 ; i < prevChildren . length ; i ++ ) {
403
+ container . removeChild ( prevChildren [ i ] . el )
404
+ }
405
+ mount ( nextChildren , container )
406
+ break
407
+ case ChildrenFlags . NO_CHILDREN :
408
+ for ( let i = 0 ; i < prevChildren . length ; i ++ ) {
409
+ container . removeChild ( prevChildren [ i ] . el )
410
+ }
411
+ break
412
+ default :
413
+ // 遍历旧的子节点,将其全部移除
414
+ for ( let i = 0 ; i < prevChildren . length ; i ++ ) {
415
+ container . removeChild ( prevChildren [ i ] . el )
416
+ }
417
+ // 遍历新的子节点,将其全部添加
418
+ for ( let i = 0 ; i < nextChildren . length ; i ++ ) {
419
+ mount ( nextChildren [ i ] , container )
420
+ }
421
+ break
422
+ }
423
+ break
424
+ }
425
+ }
426
+
289
427
const patchElement = function ( prevVNode , nextVNode , container ) {
290
- console . log ( 'hhhhhhhhhhh' )
428
+ if ( prevVNode . tag !== nextVNode . tag ) {
429
+ replaceVNode ( prevVNode , nextVNode , container )
430
+ return
431
+ }
432
+ // 拿到 el 元素,注意这时要让 nextVNode.el 也引用该元素
433
+ const el = ( nextVNode . el = prevVNode . el )
434
+ // 拿到 新旧 VNodeData
435
+ const prevData = prevVNode . data
436
+ const nextData = nextVNode . data
437
+ // 新的 VNodeData 存在时才有必要更新
438
+ if ( nextData ) {
439
+ // 遍历新的 VNodeData
440
+ for ( let key in nextData ) {
441
+ // 根据 key 拿到新旧 VNodeData 值
442
+ const prevValue = prevData [ key ]
443
+ const nextValue = nextData [ key ]
444
+ patchData ( el , key , prevValue , nextValue )
445
+ }
446
+ }
447
+ if ( prevData ) {
448
+ // 遍历旧的 VNodeData,将已经不存在于新的 VNodeData 中的数据移除
449
+ for ( let key in prevData ) {
450
+ const prevValue = prevData [ key ]
451
+ if ( prevValue && ( ! nextData || ! nextData . hasOwnProperty ( key ) ) ) {
452
+ // 第四个参数为 null,代表移除数据
453
+ patchData ( el , key , prevValue , null )
454
+ }
455
+ }
456
+ }
457
+
458
+ // 调用 patchChildren 函数递归地更新子节点
459
+ patchChildren (
460
+ prevVNode . childFlags , // 旧的 VNode 子节点的类型
461
+ nextVNode . childFlags , // 新的 VNode 子节点的类型
462
+ prevVNode . children , // 旧的 VNode 子节点
463
+ nextVNode . children , // 新的 VNode 子节点
464
+ el // 当前标签元素,即这些子节点的父节点
465
+ )
466
+ }
467
+
468
+ const patchText = function ( prevVNode , nextVNode ) {
469
+ const el = ( nextVNode . el = prevVNode . el )
470
+ if ( nextVNode . children !== prevVNode . children ) {
471
+ el . nodeValue = nextVNode . children
472
+ }
473
+ }
474
+
475
+ const patchComponent = function ( prevVNode , nextVNode , container ) {
476
+ if ( nextVNode . tag !== prevVNode . tag ) {
477
+ replaceVNode ( prevVNode , nextVNode , container )
478
+ } else if ( nextVNode . flags & VNodeFlags . COMPONENT_STATEFUL_NORMAL ) {
479
+ // 1、获取组件实例
480
+ const instance = ( nextVNode . children = prevVNode . children )
481
+ // 2、更新 props
482
+ instance . $props = nextVNode . data
483
+ // 3、更新组件
484
+ instance . _update ( )
485
+ } else {
486
+ console . log ( 'hehe' )
487
+ }
291
488
}
292
489
293
490
const patch = function ( prevVNode , nextVNode , container ) {
300
497
} else if ( nextFlags & VNodeFlags . COMPONENT ) {
301
498
patchComponent ( prevVNode , nextVNode , container )
302
499
} else if ( nextFlags & VNodeFlags . TEXT ) {
303
- patchText ( prevVNode , nextVNode , container )
500
+ patchText ( prevVNode , nextVNode )
304
501
} else if ( nextFlags & VNodeFlags . FRAGMENT ) {
305
502
patchFragment ( prevVNode , nextVNode , container )
306
503
} else if ( nextFlags & VNodeFlags . PORTAL ) {
326
523
}
327
524
}
328
525
329
- class MyComponent {
526
+ class ParentComponent {
527
+ localMsg = 'from parent'
528
+ isTrust = false
529
+
530
+ mounted ( ) {
531
+ setTimeout ( ( ) => {
532
+ this . localMsg = '2s after'
533
+ this . isTrust = true
534
+ this . _update ( )
535
+ } , 2000 ) ;
536
+ }
330
537
render ( ) {
331
- return h ( 'div' ,
332
- {
333
- style : {
334
- background : 'green'
335
- }
336
- } ,
337
- [
338
- h ( 'span' , null , '我是组件的标题1......' ) ,
339
- h ( 'span' , null , '我是组件的标题2......' )
340
- ]
341
- )
538
+ const text = this . localMsg
539
+ return h ( MyFunctionalComponent , { text : this . localMsg } )
342
540
}
343
541
}
344
- function MyFunctionalComponent ( ) {
542
+ class ChildComponent1 {
543
+ render ( ) {
544
+ return h ( 'div' , null , this . $props . text )
545
+ }
546
+ }
547
+
548
+ class ChildComponent2 {
549
+ render ( ) {
550
+ return h ( 'div' , null , '222222222' )
551
+ }
552
+ }
553
+
554
+ function MyParent ( props ) {
555
+ return h (
556
+ MyFunctionalComponent ,
557
+ { text : props . text }
558
+ )
559
+ }
560
+ function MyFunctionalComponent ( props ) {
345
561
// 返回要渲染的内容描述,即 VNode
346
562
return h (
347
563
'div' ,
350
566
background : 'red'
351
567
}
352
568
} ,
353
- [
354
- h ( 'span' , null , '我是组件的标题1......' ) ,
355
- h ( 'span' , null , '我是组件的标题2......' )
356
- ]
569
+ props . text
357
570
)
358
571
}
359
- const dynamicClass1 = { 'a' : true , 'b' : false }
360
- const dynamicClass2 = [ 'c' , 'd' ]
361
- const vnode = h ( 'div' , { style : { color : 'red' } , class : [ 'test' , dynamicClass1 ] , onClick : function ( ) { console . log ( 'click me' ) } } , [ h ( 'div' , null , [ h ( 'span' , null , 'kkk' ) , h ( Fragment , null , null ) ] ) ] )
572
+
573
+ const vnode = h ( ParentComponent , { text : '函数式组件' } , null )
362
574
363
575
render ( vnode , document . getElementById ( 'app' ) )
364
- setTimeout ( ( ) => {
365
- render ( h ( 'div' , null , [ h ( MyFunctionalComponent , null , null ) , h ( MyComponent , null , null ) ] ) , document . getElementById ( 'app' ) )
366
- } , 1000 )
576
+
367
577
368
578
</ script >
0 commit comments