Skip to content

Commit 111cb14

Browse files
authored
Merge branch 'main' into chore/move-pnpm-setting-to-pnpm-workspace
2 parents 09401ea + d79aa70 commit 111cb14

File tree

13 files changed

+563
-328
lines changed

13 files changed

+563
-328
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* **custom-element:** avoid triggering mutationObserver when relecting props ([352bc88](https://github.com/vuejs/core/commit/352bc88c1bd2fda09c61ab17ea1a5967ffcd7bc0)), closes [#12214](https://github.com/vuejs/core/issues/12214) [#12215](https://github.com/vuejs/core/issues/12215)
99
* **deps:** update dependency postcss to ^8.4.48 ([#12356](https://github.com/vuejs/core/issues/12356)) ([b5ff930](https://github.com/vuejs/core/commit/b5ff930089985a58c3553977ef999cec2a6708a4))
1010
* **hydration:** the component vnode's el should be updated when a mismatch occurs. ([#12255](https://github.com/vuejs/core/issues/12255)) ([a20a4cb](https://github.com/vuejs/core/commit/a20a4cb36a3e717d1f8f259d0d59f133f508ff0a)), closes [#12253](https://github.com/vuejs/core/issues/12253)
11-
* **reactivty:** avoid unnecessary watcher effect removal from inactive scope ([2193284](https://github.com/vuejs/core/commit/21932840eae72ffcd357a62ec596aaecc7ec224a)), closes [#5783](https://github.com/vuejs/core/issues/5783) [#5806](https://github.com/vuejs/core/issues/5806)
11+
* **reactivity:** avoid unnecessary watcher effect removal from inactive scope ([2193284](https://github.com/vuejs/core/commit/21932840eae72ffcd357a62ec596aaecc7ec224a)), closes [#5783](https://github.com/vuejs/core/issues/5783) [#5806](https://github.com/vuejs/core/issues/5806)
1212
* **reactivity:** release nested effects/scopes on effect scope stop ([#12373](https://github.com/vuejs/core/issues/12373)) ([bee2f5e](https://github.com/vuejs/core/commit/bee2f5ee62dc0cd04123b737779550726374dd0a)), closes [#12370](https://github.com/vuejs/core/issues/12370)
1313
* **runtime-dom:** set css vars before user onMounted hooks ([2d5c5e2](https://github.com/vuejs/core/commit/2d5c5e25e9b7a56e883674fb434135ac514429b5)), closes [#11533](https://github.com/vuejs/core/issues/11533)
1414
* **runtime-dom:** set css vars on update to handle child forcing reflow in onMount ([#11561](https://github.com/vuejs/core/issues/11561)) ([c4312f9](https://github.com/vuejs/core/commit/c4312f9c715c131a09e552ba46e9beb4b36d55e6))

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@
6969
"@rollup/plugin-json": "^6.1.0",
7070
"@rollup/plugin-node-resolve": "^16.0.1",
7171
"@rollup/plugin-replace": "5.0.4",
72-
"@swc/core": "^1.11.21",
72+
"@swc/core": "^1.11.24",
7373
"@types/hash-sum": "^1.0.2",
7474
"@types/node": "^22.14.1",
7575
"@types/semver": "^7.7.0",
@@ -79,7 +79,7 @@
7979
"@vue/consolidate": "1.0.0",
8080
"conventional-changelog-cli": "^5.0.0",
8181
"enquirer": "^2.4.1",
82-
"esbuild": "^0.25.3",
82+
"esbuild": "^0.25.4",
8383
"esbuild-plugin-polyfill-node": "^0.3.0",
8484
"eslint": "^9.25.1",
8585
"eslint-plugin-import-x": "^4.11.0",
@@ -97,7 +97,7 @@
9797
"pug": "^3.0.3",
9898
"puppeteer": "~24.4.0",
9999
"rimraf": "^6.0.1",
100-
"rollup": "^4.40.1",
100+
"rollup": "^4.40.2",
101101
"rollup-plugin-dts": "^6.2.1",
102102
"rollup-plugin-esbuild": "^6.2.1",
103103
"rollup-plugin-polyfill-node": "^0.13.0",

packages-private/sfc-playground/src/download/template/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"vue": "latest"
1212
},
1313
"devDependencies": {
14-
"@vitejs/plugin-vue": "^5.2.3",
15-
"vite": "^6.3.3"
14+
"@vitejs/plugin-vue": "^5.2.4",
15+
"vite": "^6.3.5"
1616
}
1717
}

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

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,4 +324,98 @@ describe('component: slots', () => {
324324
'Slot "default" invoked outside of the render function',
325325
).not.toHaveBeenWarned()
326326
})
327+
328+
test('basic warn', () => {
329+
const Comp = {
330+
setup(_: any, { slots }: any) {
331+
slots.default && slots.default()
332+
return () => null
333+
},
334+
}
335+
336+
const App = {
337+
setup() {
338+
return () => h(Comp, () => h('div'))
339+
},
340+
}
341+
342+
createApp(App).mount(nodeOps.createElement('div'))
343+
expect(
344+
'Slot "default" invoked outside of the render function',
345+
).toHaveBeenWarned()
346+
})
347+
348+
test('basic warn when mounting another app in setup', () => {
349+
const Comp = {
350+
setup(_: any, { slots }: any) {
351+
slots.default?.()
352+
return () => null
353+
},
354+
}
355+
356+
const mountComp = () => {
357+
createApp({
358+
setup() {
359+
return () => h(Comp, () => 'msg')
360+
},
361+
}).mount(nodeOps.createElement('div'))
362+
}
363+
364+
const App = {
365+
setup() {
366+
mountComp()
367+
return () => null
368+
},
369+
}
370+
371+
createApp(App).mount(nodeOps.createElement('div'))
372+
expect(
373+
'Slot "default" invoked outside of the render function',
374+
).toHaveBeenWarned()
375+
})
376+
377+
test('should not warn when render in setup', () => {
378+
const container = {
379+
setup(_: any, { slots }: any) {
380+
return () => slots.default && slots.default()
381+
},
382+
}
383+
384+
const comp = h(container, null, () => h('div'))
385+
386+
const App = {
387+
setup() {
388+
render(h(comp), nodeOps.createElement('div'))
389+
return () => null
390+
},
391+
}
392+
393+
createApp(App).mount(nodeOps.createElement('div'))
394+
expect(
395+
'Slot "default" invoked outside of the render function',
396+
).not.toHaveBeenWarned()
397+
})
398+
399+
test('basic warn when render in setup', () => {
400+
const container = {
401+
setup(_: any, { slots }: any) {
402+
slots.default && slots.default()
403+
return () => null
404+
},
405+
}
406+
407+
const comp = h(container, null, () => h('div'))
408+
409+
const App = {
410+
setup() {
411+
render(h(comp), nodeOps.createElement('div'))
412+
return () => null
413+
},
414+
}
415+
416+
createApp(App).mount(nodeOps.createElement('div'))
417+
expect(
418+
'Slot "default" invoked outside of the render function',
419+
).toHaveBeenWarned()
420+
})
327421
})

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

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
markRaw,
1111
nextTick,
1212
nodeOps,
13+
onMounted,
1314
h as originalH,
1415
ref,
1516
render,
@@ -18,6 +19,10 @@ import {
1819
} from '@vue/runtime-test'
1920
import { Fragment, createCommentVNode, createVNode } from '../../src/vnode'
2021
import { compile, createApp as createDOMApp, render as domRender } from 'vue'
22+
import type { HMRRuntime } from '../../src/hmr'
23+
24+
declare var __VUE_HMR_RUNTIME__: HMRRuntime
25+
const { rerender, createRecord } = __VUE_HMR_RUNTIME__
2126

2227
describe('renderer: teleport', () => {
2328
describe('eager mode', () => {
@@ -741,4 +746,56 @@ describe('renderer: teleport', () => {
741746
expect(tRefInMounted).toBe(target.children[1])
742747
})
743748
}
749+
750+
test('handle update and hmr rerender', async () => {
751+
const target = document.createElement('div')
752+
const root = document.createElement('div')
753+
754+
const Comp = {
755+
setup() {
756+
const cls = ref('foo')
757+
onMounted(() => {
758+
// trigger update
759+
cls.value = 'bar'
760+
})
761+
return { cls, target }
762+
},
763+
template: `
764+
<Teleport :to="target">
765+
<div :class="cls">
766+
<div>
767+
<slot></slot>
768+
</div>
769+
</div>
770+
</Teleport>
771+
`,
772+
}
773+
774+
const appId = 'test-app-id'
775+
const App = {
776+
__hmrId: appId,
777+
components: { Comp },
778+
render() {
779+
return originalH(Comp, null, { default: () => originalH('div', 'foo') })
780+
},
781+
}
782+
createRecord(appId, App)
783+
784+
domRender(originalH(App), root)
785+
expect(target.innerHTML).toBe(
786+
'<div class="foo"><div><div>foo</div></div></div>',
787+
)
788+
await nextTick()
789+
expect(target.innerHTML).toBe(
790+
'<div class="bar"><div><div>foo</div></div></div>',
791+
)
792+
793+
rerender(appId, () =>
794+
originalH(Comp, null, { default: () => originalH('div', 'bar') }),
795+
)
796+
await nextTick()
797+
expect(target.innerHTML).toBe(
798+
'<div class="bar"><div><div>bar</div></div></div>',
799+
)
800+
})
744801
})

packages/runtime-core/__tests__/helpers/renderList.spec.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
import { isReactive, reactive, shallowReactive } from '../../src/index'
1+
import {
2+
effect,
3+
isReactive,
4+
reactive,
5+
readonly,
6+
shallowReactive,
7+
} from '../../src/index'
28
import { renderList } from '../../src/helpers/renderList'
39

410
describe('renderList', () => {
@@ -65,4 +71,31 @@ describe('renderList', () => {
6571
const shallowReactiveArray = shallowReactive([{ foo: 1 }])
6672
expect(renderList(shallowReactiveArray, isReactive)).toEqual([false])
6773
})
74+
75+
it('should not allow mutation', () => {
76+
const arr = readonly(reactive([{ foo: 1 }]))
77+
expect(
78+
renderList(arr, item => {
79+
;(item as any).foo = 0
80+
return item.foo
81+
}),
82+
).toEqual([1])
83+
expect(
84+
`Set operation on key "foo" failed: target is readonly.`,
85+
).toHaveBeenWarned()
86+
})
87+
88+
it('should trigger effect for deep mutations in readonly reactive arrays', () => {
89+
const arr = reactive([{ foo: 1 }])
90+
const readonlyArr = readonly(arr)
91+
92+
let dummy
93+
effect(() => {
94+
dummy = renderList(readonlyArr, item => item.foo)
95+
})
96+
expect(dummy).toEqual([1])
97+
98+
arr[0].foo = 2
99+
expect(dummy).toEqual([2])
100+
})
68101
})

packages/runtime-core/src/component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -806,7 +806,7 @@ export function setupComponent(
806806
const { props, children } = instance.vnode
807807
const isStateful = isStatefulComponent(instance)
808808
initProps(instance, props, isStateful, isSSR)
809-
initSlots(instance, children, optimized)
809+
initSlots(instance, children, optimized || isSSR)
810810

811811
const setupResult = isStateful
812812
? setupStatefulComponent(instance, isSSR)

packages/runtime-core/src/componentSlots.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@ import {
1717
} from '@vue/shared'
1818
import { warn } from './warning'
1919
import { isKeepAlive } from './components/KeepAlive'
20-
import { type ContextualRenderFn, withCtx } from './componentRenderContext'
20+
import {
21+
type ContextualRenderFn,
22+
currentRenderingInstance,
23+
withCtx,
24+
} from './componentRenderContext'
2125
import { isHmrUpdating } from './hmr'
2226
import { DeprecationTypes, isCompatEnabled } from './compat/compatConfig'
2327
import { TriggerOpTypes, trigger } from '@vue/reactivity'
@@ -102,7 +106,8 @@ const normalizeSlot = (
102106
if (
103107
__DEV__ &&
104108
currentInstance &&
105-
(!ctx || ctx.root === currentInstance.root)
109+
!(ctx === null && currentRenderingInstance) &&
110+
!(ctx && ctx.root !== currentInstance.root)
106111
) {
107112
warn(
108113
`Slot "${key}" invoked outside of the render function: ` +

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,8 @@ export const TeleportImpl = {
220220
// even in block tree mode we need to make sure all root-level nodes
221221
// in the teleport inherit previous DOM references so that they can
222222
// be moved in future patches.
223-
traverseStaticChildren(n1, n2, true)
223+
// in dev mode, deep traversal is necessary for HMR
224+
traverseStaticChildren(n1, n2, !__DEV__)
224225
} else if (!optimized) {
225226
patchChildren(
226227
n1,

packages/runtime-core/src/helpers/renderList.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import type { VNode, VNodeChild } from '../vnode'
22
import {
33
isReactive,
4+
isReadonly,
45
isShallow,
56
shallowReadArray,
67
toReactive,
8+
toReadonly,
79
} from '@vue/reactivity'
810
import { isArray, isObject, isString } from '@vue/shared'
911
import { warn } from '../warning'
@@ -69,14 +71,20 @@ export function renderList(
6971
if (sourceIsArray || isString(source)) {
7072
const sourceIsReactiveArray = sourceIsArray && isReactive(source)
7173
let needsWrap = false
74+
let isReadonlySource = false
7275
if (sourceIsReactiveArray) {
7376
needsWrap = !isShallow(source)
77+
isReadonlySource = isReadonly(source)
7478
source = shallowReadArray(source)
7579
}
7680
ret = new Array(source.length)
7781
for (let i = 0, l = source.length; i < l; i++) {
7882
ret[i] = renderItem(
79-
needsWrap ? toReactive(source[i]) : source[i],
83+
needsWrap
84+
? isReadonlySource
85+
? toReadonly(toReactive(source[i]))
86+
: toReactive(source[i])
87+
: source[i],
8088
i,
8189
undefined,
8290
cached && cached[i],

packages/server-renderer/__tests__/ssrSlot.spec.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { createApp } from 'vue'
1+
import { createApp, defineAsyncComponent, h } from 'vue'
22
import { renderToString } from '../src/renderToString'
33

44
const components = {
@@ -154,6 +154,38 @@ describe('ssr: slot', () => {
154154
).toBe(`<div><p>1</p><p>2</p></div>`)
155155
})
156156

157+
// #12438
158+
test('async component slot with v-if true', async () => {
159+
const Layout = defineAsyncComponent(() =>
160+
Promise.resolve({
161+
template: `<div><slot name="header">default header</slot></div>`,
162+
}),
163+
)
164+
const LayoutLoader = {
165+
setup(_: any, context: any) {
166+
return () => h(Layout, {}, context.slots)
167+
},
168+
}
169+
expect(
170+
await renderToString(
171+
createApp({
172+
components: {
173+
LayoutLoader,
174+
},
175+
template: `
176+
<Suspense>
177+
<LayoutLoader>
178+
<template v-if="true" #header>
179+
new header
180+
</template>
181+
</LayoutLoader>
182+
</Suspense>
183+
`,
184+
}),
185+
),
186+
).toBe(`<div><!--[--> new header <!--]--></div>`)
187+
})
188+
157189
// #11326
158190
test('dynamic component slot', async () => {
159191
expect(

0 commit comments

Comments
 (0)