@@ -7,7 +7,10 @@ import {
7
7
Teleport ,
8
8
Transition ,
9
9
type VNode ,
10
+ createBlock ,
10
11
createCommentVNode ,
12
+ createElementBlock ,
13
+ createElementVNode ,
11
14
createSSRApp ,
12
15
createStaticVNode ,
13
16
createTextVNode ,
@@ -17,16 +20,19 @@ import {
17
20
h ,
18
21
nextTick ,
19
22
onMounted ,
23
+ openBlock ,
20
24
ref ,
21
25
renderSlot ,
22
26
useCssVars ,
23
27
vModelCheckbox ,
24
28
vShow ,
29
+ withCtx ,
25
30
withDirectives ,
26
31
} from '@vue/runtime-dom'
27
32
import { type SSRContext , renderToString } from '@vue/server-renderer'
28
33
import { PatchFlags } from '@vue/shared'
29
34
import { vShowOriginalDisplay } from '../../runtime-dom/src/directives/vShow'
35
+ import { expect } from 'vitest'
30
36
31
37
function mountWithHydration ( html : string , render : ( ) => any ) {
32
38
const container = document . createElement ( 'div' )
@@ -1292,6 +1298,81 @@ describe('SSR hydration', () => {
1292
1298
` )
1293
1299
} )
1294
1300
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
+
1295
1376
describe ( 'mismatch handling' , ( ) => {
1296
1377
test ( 'text node' , ( ) => {
1297
1378
const { container } = mountWithHydration ( `foo` , ( ) => 'bar' )
0 commit comments