@@ -7,7 +7,10 @@ import {
77 Teleport ,
88 Transition ,
99 type VNode ,
10+ createBlock ,
1011 createCommentVNode ,
12+ createElementBlock ,
13+ createElementVNode ,
1114 createSSRApp ,
1215 createStaticVNode ,
1316 createTextVNode ,
@@ -17,16 +20,19 @@ import {
1720 h ,
1821 nextTick ,
1922 onMounted ,
23+ openBlock ,
2024 ref ,
2125 renderSlot ,
2226 useCssVars ,
2327 vModelCheckbox ,
2428 vShow ,
29+ withCtx ,
2530 withDirectives ,
2631} from '@vue/runtime-dom'
2732import { type SSRContext , renderToString } from '@vue/server-renderer'
2833import { PatchFlags } from '@vue/shared'
2934import { vShowOriginalDisplay } from '../../runtime-dom/src/directives/vShow'
35+ import { expect } from 'vitest'
3036
3137function mountWithHydration ( html : string , render : ( ) => any ) {
3238 const container = document . createElement ( 'div' )
@@ -1292,6 +1298,81 @@ describe('SSR hydration', () => {
12921298 ` )
12931299 } )
12941300
1301+ // #10607
1302+ test ( 'update component stable slot (prod + optimized mode)' , async ( ) => {
1303+ __DEV__ = false
1304+ const container = document . createElement ( 'div' )
1305+ container . innerHTML = `<template><div show="false"><!--[--><div><div><!----></div></div><div>0</div><!--]--></div></template>`
1306+ const Comp = {
1307+ render ( this : any ) {
1308+ return (
1309+ openBlock ( ) ,
1310+ createElementBlock ( 'div' , null , [ renderSlot ( this . $slots , 'default' ) ] )
1311+ )
1312+ } ,
1313+ }
1314+ const show = ref ( false )
1315+ const clicked = ref ( false )
1316+
1317+ const Wrapper = {
1318+ setup ( ) {
1319+ const items = ref < number [ ] > ( [ ] )
1320+ onMounted ( ( ) => {
1321+ items . value = [ 1 ]
1322+ } )
1323+ return ( ) => {
1324+ return (
1325+ openBlock ( ) ,
1326+ createBlock ( Comp , null , {
1327+ default : withCtx ( ( ) => [
1328+ createElementVNode ( 'div' , null , [
1329+ createElementVNode ( 'div' , null , [
1330+ clicked . value
1331+ ? ( openBlock ( ) ,
1332+ createElementBlock ( 'div' , { key : 0 } , 'foo' ) )
1333+ : createCommentVNode ( 'v-if' , true ) ,
1334+ ] ) ,
1335+ ] ) ,
1336+ createElementVNode (
1337+ 'div' ,
1338+ null ,
1339+ items . value . length ,
1340+ 1 /* TEXT */ ,
1341+ ) ,
1342+ ] ) ,
1343+ _ : 1 /* STABLE */ ,
1344+ } )
1345+ )
1346+ }
1347+ } ,
1348+ }
1349+ createSSRApp ( {
1350+ components : { Wrapper } ,
1351+ data ( ) {
1352+ return { show }
1353+ } ,
1354+ template : `<Wrapper :show="show"/>` ,
1355+ } ) . mount ( container )
1356+
1357+ await nextTick ( )
1358+ expect ( container . innerHTML ) . toBe (
1359+ `<div show="false"><!--[--><div><div><!----></div></div><div>1</div><!--]--></div>` ,
1360+ )
1361+
1362+ show . value = true
1363+ await nextTick ( )
1364+ expect ( async ( ) => {
1365+ clicked . value = true
1366+ await nextTick ( )
1367+ } ) . not . toThrow ( "Cannot read properties of null (reading 'insertBefore')" )
1368+
1369+ await nextTick ( )
1370+ expect ( container . innerHTML ) . toBe (
1371+ `<div show="true"><!--[--><div><div><div>foo</div></div></div><div>1</div><!--]--></div>` ,
1372+ )
1373+ __DEV__ = true
1374+ } )
1375+
12951376 describe ( 'mismatch handling' , ( ) => {
12961377 test ( 'text node' , ( ) => {
12971378 const { container } = mountWithHydration ( `foo` , ( ) => 'bar' )
0 commit comments