Skip to content

Commit c19232d

Browse files
authored
Merge branch 'main' into edison/fix/hmrRootReload
2 parents 5562a81 + 6264505 commit c19232d

File tree

10 files changed

+215
-29
lines changed

10 files changed

+215
-29
lines changed

packages/compiler-sfc/__tests__/compileScript/__snapshots__/definePropsDestructure.spec.ts.snap

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,25 @@ return () => {}
192192
}"
193193
`;
194194

195+
exports[`sfc reactive props destructure > handle function parameters with same name as destructured props 1`] = `
196+
"
197+
export default {
198+
setup(__props) {
199+
200+
201+
function test(value) {
202+
try {
203+
} catch {
204+
}
205+
}
206+
console.log(__props.value)
207+
208+
return () => {}
209+
}
210+
211+
}"
212+
`;
213+
195214
exports[`sfc reactive props destructure > multi-variable declaration 1`] = `
196215
"
197216
export default {

packages/compiler-sfc/__tests__/compileScript/definePropsDestructure.spec.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,22 @@ describe('sfc reactive props destructure', () => {
358358
expect(content).toMatch(`props: ['item'],`)
359359
})
360360

361+
test('handle function parameters with same name as destructured props', () => {
362+
const { content } = compile(`
363+
<script setup>
364+
const { value } = defineProps()
365+
function test(value) {
366+
try {
367+
} catch {
368+
}
369+
}
370+
console.log(value)
371+
</script>
372+
`)
373+
assertCode(content)
374+
expect(content).toMatch(`console.log(__props.value)`)
375+
})
376+
361377
test('defineProps/defineEmits in multi-variable declaration (full removal)', () => {
362378
const { content } = compile(`
363379
<script setup>

packages/compiler-sfc/src/script/definePropsDestructure.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,8 @@ export function transformDestructuredProps(
291291
parent && parentStack.pop()
292292
if (
293293
(node.type === 'BlockStatement' && !isFunctionType(parent!)) ||
294-
isFunctionType(node)
294+
isFunctionType(node) ||
295+
node.type === 'CatchClause'
295296
) {
296297
popScope()
297298
}

packages/runtime-core/__tests__/components/BaseTransition.spec.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1198,4 +1198,51 @@ describe('BaseTransition', () => {
11981198
test('should not error on KeepAlive w/ function children', () => {
11991199
expect(() => mount({}, () => () => h('div'), true)).not.toThrow()
12001200
})
1201+
1202+
// #12465
1203+
test('mode: "out-in" w/ KeepAlive + fallthrough attrs (prod mode)', async () => {
1204+
__DEV__ = false
1205+
async function testOutIn({ trueBranch, falseBranch }: ToggleOptions) {
1206+
const toggle = ref(true)
1207+
const { props, cbs } = mockProps({ mode: 'out-in' }, true)
1208+
const root = nodeOps.createElement('div')
1209+
const App = {
1210+
render() {
1211+
return h(
1212+
BaseTransition,
1213+
{
1214+
...props,
1215+
class: 'test',
1216+
},
1217+
() =>
1218+
h(KeepAlive, null, toggle.value ? trueBranch() : falseBranch()),
1219+
)
1220+
},
1221+
}
1222+
render(h(App), root)
1223+
1224+
expect(serializeInner(root)).toBe(`<div class="test">0</div>`)
1225+
1226+
// trigger toggle
1227+
toggle.value = false
1228+
await nextTick()
1229+
expect(props.onBeforeLeave).toHaveBeenCalledTimes(1)
1230+
expect(serialize((props.onBeforeLeave as any).mock.calls[0][0])).toBe(
1231+
`<div class="test">0</div>`,
1232+
)
1233+
expect(props.onLeave).toHaveBeenCalledTimes(1)
1234+
expect(serialize((props.onLeave as any).mock.calls[0][0])).toBe(
1235+
`<div class="test">0</div>`,
1236+
)
1237+
expect(props.onAfterLeave).not.toHaveBeenCalled()
1238+
// enter should not have started
1239+
expect(props.onBeforeEnter).not.toHaveBeenCalled()
1240+
expect(props.onEnter).not.toHaveBeenCalled()
1241+
expect(props.onAfterEnter).not.toHaveBeenCalled()
1242+
cbs.doneLeave[`<div class="test">0</div>`]()
1243+
expect(serializeInner(root)).toBe(`<span class="test">0</span>`)
1244+
}
1245+
await runTestWithKeepAlive(testOutIn)
1246+
__DEV__ = true
1247+
})
12011248
})

packages/runtime-core/__tests__/components/Teleport.spec.ts

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,19 @@ import {
1414
h as originalH,
1515
ref,
1616
render,
17+
serialize,
1718
serializeInner,
1819
withDirectives,
1920
} from '@vue/runtime-test'
20-
import { Fragment, createCommentVNode, createVNode } from '../../src/vnode'
21+
import {
22+
Fragment,
23+
createBlock,
24+
createCommentVNode,
25+
createTextVNode,
26+
createVNode,
27+
openBlock,
28+
} from '../../src/vnode'
29+
import { toDisplayString } from '@vue/shared'
2130
import { compile, createApp as createDOMApp, render as domRender } from 'vue'
2231
import type { HMRRuntime } from '../../src/hmr'
2332

@@ -248,6 +257,39 @@ describe('renderer: teleport', () => {
248257
expect(serializeInner(target)).toBe(`teleported`)
249258
})
250259

260+
test('should traverse comment node after updating in optimize mode', async () => {
261+
const target = nodeOps.createElement('div')
262+
const root = nodeOps.createElement('div')
263+
const count = ref(0)
264+
let teleport
265+
266+
__DEV__ = false
267+
render(
268+
h(() => {
269+
teleport =
270+
(openBlock(),
271+
createBlock(Teleport, { to: target }, [
272+
createCommentVNode('comment in teleport'),
273+
]))
274+
return h('div', null, [
275+
createTextVNode(toDisplayString(count.value)),
276+
teleport,
277+
])
278+
}),
279+
root,
280+
)
281+
const commentNode = teleport!.children[0].el
282+
expect(serializeInner(root)).toBe(`<div>0</div>`)
283+
expect(serializeInner(target)).toBe(`<!--comment in teleport-->`)
284+
expect(serialize(commentNode)).toBe(`<!--comment in teleport-->`)
285+
286+
count.value = 1
287+
await nextTick()
288+
__DEV__ = true
289+
expect(serializeInner(root)).toBe(`<div>1</div>`)
290+
expect(teleport!.children[0].el).toBe(commentNode)
291+
})
292+
251293
test('should remove children when unmounted', () => {
252294
const target = nodeOps.createElement('div')
253295
const root = nodeOps.createElement('div')
@@ -274,6 +316,34 @@ describe('renderer: teleport', () => {
274316
testUnmount({ to: null, disabled: true })
275317
})
276318

319+
// #10747
320+
test('should unmount correctly when using top level comment in teleport', async () => {
321+
const target = nodeOps.createElement('div')
322+
const root = nodeOps.createElement('div')
323+
const count = ref(0)
324+
325+
__DEV__ = false
326+
render(
327+
h(() => {
328+
return h('div', null, [
329+
createTextVNode(toDisplayString(count.value)),
330+
(openBlock(),
331+
createBlock(Teleport, { to: target }, [
332+
createCommentVNode('comment in teleport'),
333+
])),
334+
])
335+
}),
336+
root,
337+
)
338+
339+
count.value = 1
340+
341+
await nextTick()
342+
__DEV__ = true
343+
render(null, root)
344+
expect(root.children.length).toBe(0)
345+
})
346+
277347
test('component with multi roots should be removed when unmounted', () => {
278348
const target = nodeOps.createElement('div')
279349
const root = nodeOps.createElement('div')

packages/runtime-core/__tests__/hydration.spec.ts

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,13 @@ import {
3232
withCtx,
3333
withDirectives,
3434
} from '@vue/runtime-dom'
35+
import type { HMRRuntime } from '../src/hmr'
3536
import { type SSRContext, renderToString } from '@vue/server-renderer'
3637
import { PatchFlags, normalizeStyle } from '@vue/shared'
3738
import { vShowOriginalDisplay } from '../../runtime-dom/src/directives/vShow'
38-
import type { HMRRuntime } from '../src/hmr'
3939

4040
declare var __VUE_HMR_RUNTIME__: HMRRuntime
41-
const { reload } = __VUE_HMR_RUNTIME__
41+
const { createRecord, reload } = __VUE_HMR_RUNTIME__
4242

4343
function mountWithHydration(html: string, render: () => any) {
4444
const container = document.createElement('div')
@@ -1846,6 +1846,40 @@ describe('SSR hydration', () => {
18461846
}
18471847
})
18481848

1849+
test('hmr reload child wrapped in KeepAlive', async () => {
1850+
const id = 'child-reload'
1851+
const Child = {
1852+
__hmrId: id,
1853+
template: `<div>foo</div>`,
1854+
}
1855+
createRecord(id, Child)
1856+
1857+
const appId = 'test-app-id'
1858+
const App = {
1859+
__hmrId: appId,
1860+
components: { Child },
1861+
template: `
1862+
<div>
1863+
<KeepAlive>
1864+
<Child />
1865+
</KeepAlive>
1866+
</div>
1867+
`,
1868+
}
1869+
1870+
const root = document.createElement('div')
1871+
root.innerHTML = await renderToString(h(App))
1872+
createSSRApp(App).mount(root)
1873+
expect(root.innerHTML).toBe('<div><div>foo</div></div>')
1874+
1875+
reload(id, {
1876+
__hmrId: id,
1877+
template: `<div>bar</div>`,
1878+
})
1879+
await nextTick()
1880+
expect(root.innerHTML).toBe('<div><div>bar</div></div>')
1881+
})
1882+
18491883
test('hmr root reload', async () => {
18501884
const appId = 'test-app-id'
18511885
const App = {

packages/runtime-core/src/components/BaseTransition.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -501,9 +501,8 @@ function getInnerChild(vnode: VNode): VNode | undefined {
501501

502502
return vnode
503503
}
504-
// #7121 ensure get the child component subtree in case
505-
// it's been replaced during HMR
506-
if (__DEV__ && vnode.component) {
504+
// #7121,#12465 get the component subtree if it's been mounted
505+
if (vnode.component) {
507506
return vnode.component.subTree
508507
}
509508

packages/runtime-core/src/renderer.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1208,12 +1208,12 @@ function baseCreateRenderer(
12081208
}
12091209
}
12101210

1211+
// avoid hydration for hmr updating
1212+
if (__DEV__ && isHmrUpdating) initialVNode.el = null
1213+
12111214
// setup() is async. This component relies on async logic to be resolved
12121215
// before proceeding
12131216
if (__FEATURE_SUSPENSE__ && instance.asyncDep) {
1214-
// avoid hydration for hmr updating
1215-
if (__DEV__ && isHmrUpdating) initialVNode.el = null
1216-
12171217
parentSuspense &&
12181218
parentSuspense.registerDep(instance, setupRenderEffect, optimized)
12191219

@@ -2503,13 +2503,13 @@ export function traverseStaticChildren(
25032503
if (c2.type === Text) {
25042504
c2.el = c1.el
25052505
}
2506-
if (__DEV__) {
2507-
// #2324 also inherit for comment nodes, but not placeholders (e.g. v-if which
2508-
// would have received .el during block patch)
2509-
if (c2.type === Comment && !c2.el) {
2510-
c2.el = c1.el
2511-
}
2506+
// #2324 also inherit for comment nodes, but not placeholders (e.g. v-if which
2507+
// would have received .el during block patch)
2508+
if (c2.type === Comment && !c2.el) {
2509+
c2.el = c1.el
2510+
}
25122511

2512+
if (__DEV__) {
25132513
c2.el && (c2.el.__vnode = c2)
25142514
}
25152515
}

0 commit comments

Comments
 (0)