@@ -25,6 +25,7 @@ import {
2525const EMPTY_ARR = [ ] ;
2626const isArray = Array . isArray ;
2727const assign = Object . assign ;
28+ const EMPTY_STR = '' ;
2829
2930// Global state for the current render pass
3031let beforeDiff , afterDiff , renderHook , ummountHook ;
@@ -65,8 +66,8 @@ export function renderToString(vnode, context, _rendererState) {
6566 _rendererState
6667 ) ;
6768
68- if ( Array . isArray ( rendered ) ) {
69- return rendered . join ( '' ) ;
69+ if ( isArray ( rendered ) ) {
70+ return rendered . join ( EMPTY_STR ) ;
7071 }
7172 return rendered ;
7273 } catch ( e ) {
@@ -119,7 +120,7 @@ export async function renderToStringAsync(vnode, context) {
119120 undefined
120121 ) ;
121122
122- if ( Array . isArray ( rendered ) ) {
123+ if ( isArray ( rendered ) ) {
123124 let count = 0 ;
124125 let resolved = rendered ;
125126
@@ -133,7 +134,7 @@ export async function renderToStringAsync(vnode, context) {
133134 resolved = ( await Promise . all ( resolved ) ) . flat ( ) ;
134135 }
135136
136- return resolved . join ( '' ) ;
137+ return resolved . join ( EMPTY_STR ) ;
137138 }
138139
139140 return rendered ;
@@ -226,19 +227,26 @@ function _renderToString(
226227 renderer
227228) {
228229 // Ignore non-rendered VNodes/values
229- if ( vnode == null || vnode === true || vnode === false || vnode === '' ) {
230- return '' ;
230+ if (
231+ vnode == null ||
232+ vnode === true ||
233+ vnode === false ||
234+ vnode === EMPTY_STR
235+ ) {
236+ return EMPTY_STR ;
231237 }
232238
233239 // Text VNodes: escape as HTML
234240 if ( typeof vnode !== 'object' ) {
235- if ( typeof vnode === 'function' ) return '' ;
236- return encodeEntities ( vnode + '' ) ;
241+ if ( typeof vnode === 'function' ) return EMPTY_STR ;
242+ return typeof vnode === 'string'
243+ ? encodeEntities ( vnode )
244+ : vnode + EMPTY_STR ;
237245 }
238246
239247 // Recurse into children / Arrays
240248 if ( isArray ( vnode ) ) {
241- let rendered = '' ,
249+ let rendered = EMPTY_STR ,
242250 renderArray ;
243251 parent [ CHILDREN ] = vnode ;
244252 for ( let i = 0 ; i < vnode . length ; i ++ ) {
@@ -256,15 +264,15 @@ function _renderToString(
256264 ) ;
257265
258266 if ( typeof childRender === 'string' ) {
259- rendered += childRender ;
267+ rendered = rendered + childRender ;
260268 } else {
261269 renderArray = renderArray || [ ] ;
262270
263271 if ( rendered ) renderArray . push ( rendered ) ;
264272
265- rendered = '' ;
273+ rendered = EMPTY_STR ;
266274
267- if ( Array . isArray ( childRender ) ) {
275+ if ( isArray ( childRender ) ) {
268276 renderArray . push ( ...childRender ) ;
269277 } else {
270278 renderArray . push ( childRender ) ;
@@ -281,7 +289,7 @@ function _renderToString(
281289 }
282290
283291 // VNodes have {constructor:undefined} to prevent JSON injection:
284- if ( vnode . constructor !== undefined ) return '' ;
292+ if ( vnode . constructor !== undefined ) return EMPTY_STR ;
285293
286294 vnode [ PARENT ] = parent ;
287295 if ( beforeDiff ) beforeDiff ( vnode ) ;
@@ -298,9 +306,9 @@ function _renderToString(
298306 if ( type === Fragment ) {
299307 // Serialized precompiled JSX.
300308 if ( props . tpl ) {
301- let out = '' ;
309+ let out = EMPTY_STR ;
302310 for ( let i = 0 ; i < props . tpl . length ; i ++ ) {
303- out += props . tpl [ i ] ;
311+ out = out + props . tpl [ i ] ;
304312
305313 if ( props . exprs && i < props . exprs . length ) {
306314 const value = props . exprs [ i ] ;
@@ -311,18 +319,20 @@ function _renderToString(
311319 typeof value === 'object' &&
312320 ( value . constructor === undefined || isArray ( value ) )
313321 ) {
314- out += _renderToString (
315- value ,
316- context ,
317- isSvgMode ,
318- selectValue ,
319- vnode ,
320- asyncMode ,
321- renderer
322- ) ;
322+ out =
323+ out +
324+ _renderToString (
325+ value ,
326+ context ,
327+ isSvgMode ,
328+ selectValue ,
329+ vnode ,
330+ asyncMode ,
331+ renderer
332+ ) ;
323333 } else {
324334 // Values are pre-escaped by the JSX transform
325- out += value ;
335+ out = out + value ;
326336 }
327337 }
328338 }
@@ -331,7 +341,9 @@ function _renderToString(
331341 } else if ( props . UNSTABLE_comment ) {
332342 // Fragments are the least used components of core that's why
333343 // branching here for comments has the least effect on perf.
334- return '<!--' + encodeEntities ( props . UNSTABLE_comment || '' ) + '-->' ;
344+ return (
345+ '<!--' + encodeEntities ( props . UNSTABLE_comment || EMPTY_STR ) + '-->'
346+ ) ;
335347 }
336348
337349 rendered = props . children ;
@@ -342,11 +354,13 @@ function _renderToString(
342354 cctx = provider ? provider . props . value : contextType . __ ;
343355 }
344356
345- if ( type . prototype && typeof type . prototype . render === 'function' ) {
357+ let isClassComponent =
358+ type . prototype && typeof type . prototype . render === 'function' ;
359+ if ( isClassComponent ) {
346360 rendered = /**#__NOINLINE__**/ renderClassComponent ( vnode , cctx ) ;
347361 component = vnode [ COMPONENT ] ;
348362 } else {
349- component = {
363+ vnode [ COMPONENT ] = component = {
350364 __v : vnode ,
351365 props,
352366 context : cctx ,
@@ -357,7 +371,6 @@ function _renderToString(
357371 // hooks
358372 __h : [ ]
359373 } ;
360- vnode [ COMPONENT ] = component ;
361374
362375 // If a hook invokes setState() to invalidate the component during rendering,
363376 // re-render it up to 25 times to allow "settling" of memoized states.
@@ -380,10 +393,10 @@ function _renderToString(
380393 }
381394
382395 if (
383- ( type . getDerivedStateFromError || component . componentDidCatch ) &&
384- options . errorBoundaries
396+ isClassComponent &&
397+ options . errorBoundaries &&
398+ ( type . getDerivedStateFromError || component . componentDidCatch )
385399 ) {
386- let str = '' ;
387400 // When a component returns a Fragment node we flatten it in core, so we
388401 // need to mirror that logic here too
389402 let isTopLevelFragment =
@@ -393,7 +406,7 @@ function _renderToString(
393406 rendered = isTopLevelFragment ? rendered . props . children : rendered ;
394407
395408 try {
396- str = _renderToString (
409+ return _renderToString (
397410 rendered ,
398411 context ,
399412 isSvgMode ,
@@ -402,14 +415,15 @@ function _renderToString(
402415 asyncMode ,
403416 renderer
404417 ) ;
405- return str ;
406418 } catch ( err ) {
419+ let str = EMPTY_STR ;
420+
407421 if ( type . getDerivedStateFromError ) {
408422 component [ NEXT_STATE ] = type . getDerivedStateFromError ( err ) ;
409423 }
410424
411425 if ( component . componentDidCatch ) {
412- component . componentDidCatch ( err , { } ) ;
426+ component . componentDidCatch ( err , EMPTY_OBJ ) ;
413427 }
414428
415429 if ( component [ DIRTY ] ) {
@@ -493,7 +507,7 @@ function _renderToString(
493507
494508 let errorHook = options [ CATCH_ERROR ] ;
495509 if ( errorHook ) errorHook ( error , vnode ) ;
496- return '' ;
510+ return EMPTY_STR ;
497511 }
498512
499513 if ( ! asyncMode ) throw error ;
@@ -525,23 +539,25 @@ function _renderToString(
525539 asyncMode ,
526540 renderer
527541 ) ,
528- ( ) => renderNestedChildren ( )
542+ renderNestedChildren
529543 ) ;
530544 }
531545 } ;
532546
533- return error . then ( ( ) => renderNestedChildren ( ) ) ;
547+ return error . then ( renderNestedChildren ) ;
534548 }
535549 }
536550
537551 // Serialize Element VNodes to HTML
538552 let s = '<' + type ,
539- html = '' ,
553+ html = EMPTY_STR ,
540554 children ;
541555
542556 for ( let name in props ) {
543557 let v = props [ name ] ;
544558
559+ if ( typeof v === 'function' ) continue ;
560+
545561 switch ( name ) {
546562 case 'children' :
547563 children = v ;
@@ -622,7 +638,7 @@ function _renderToString(
622638 // serialize boolean aria-xyz or draggable attribute values as strings
623639 // `draggable` is an enumerated attribute and not Boolean. A value of `true` or `false` is mandatory
624640 // https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/draggable
625- v += '' ;
641+ v = v + EMPTY_STR ;
626642 } else if ( isSvgMode ) {
627643 if ( SVG_CAMEL_CASE . test ( name ) ) {
628644 name =
@@ -637,11 +653,17 @@ function _renderToString(
637653 }
638654
639655 // write this attribute to the buffer
640- if ( v != null && v !== false && typeof v !== 'function' ) {
641- if ( v === true || v === '' ) {
656+ if ( v != null && v !== false ) {
657+ if ( v === true || v === EMPTY_STR ) {
642658 s = s + ' ' + name ;
643659 } else {
644- s = s + ' ' + name + '="' + encodeEntities ( v + '' ) + '"' ;
660+ s =
661+ s +
662+ ' ' +
663+ name +
664+ '="' +
665+ ( typeof v === 'string' ? encodeEntities ( v ) : v + EMPTY_STR ) +
666+ '"' ;
645667 }
646668 }
647669 }
@@ -687,7 +709,7 @@ function _renderToString(
687709 const endTag = '</' + type + '>' ;
688710 const startTag = s + '>' ;
689711
690- if ( Array . isArray ( html ) ) return [ startTag , ...html , endTag ] ;
712+ if ( isArray ( html ) ) return [ startTag , ...html , endTag ] ;
691713 else if ( typeof html !== 'string' ) return [ startTag , html , endTag ] ;
692714 return startTag + html + endTag ;
693715}
0 commit comments