Skip to content

Commit f1d1831

Browse files
authored
fix(custom-element): handle keys set on custom elements (#11655)
close #11641
1 parent 1d988b5 commit f1d1831

File tree

3 files changed

+102
-46
lines changed

3 files changed

+102
-46
lines changed

packages/runtime-dom/src/apiCustomElement.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,10 @@ export class VueElement
487487
delete this._props[key]
488488
} else {
489489
this._props[key] = val
490+
// support set key on ceVNode
491+
if (key === 'key' && this._app) {
492+
this._app._ceVNode!.key = val
493+
}
490494
}
491495
if (shouldUpdate && this._instance) {
492496
this._update()

packages/vue/__tests__/e2e/ssr-custom-element.html

Lines changed: 0 additions & 44 deletions
This file was deleted.

packages/vue/__tests__/e2e/ssr-custom-element.spec.ts

Lines changed: 98 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,57 @@ import { setupPuppeteer } from './e2eUtils'
33

44
const { page, click, text } = setupPuppeteer()
55

6+
beforeEach(async () => {
7+
await page().addScriptTag({
8+
path: path.resolve(__dirname, '../../dist/vue.global.js'),
9+
})
10+
})
11+
12+
async function setContent(html: string) {
13+
await page().setContent(`<div id="app">${html}</div>`)
14+
}
15+
616
// this must be tested in actual Chrome because jsdom does not support
717
// declarative shadow DOM
818
test('ssr custom element hydration', async () => {
9-
await page().goto(
10-
`file://${path.resolve(__dirname, './ssr-custom-element.html')}`,
19+
await setContent(
20+
`<my-element><template shadowrootmode="open"><button>1</button></template></my-element><my-element-async><template shadowrootmode="open"><button>1</button></template></my-element-async>`,
1121
)
1222

23+
await page().evaluate(() => {
24+
const {
25+
h,
26+
ref,
27+
defineSSRCustomElement,
28+
defineAsyncComponent,
29+
onMounted,
30+
useHost,
31+
} = (window as any).Vue
32+
33+
const def = {
34+
setup() {
35+
const count = ref(1)
36+
const el = useHost()
37+
onMounted(() => (el.style.border = '1px solid red'))
38+
39+
return () => h('button', { onClick: () => count.value++ }, count.value)
40+
},
41+
}
42+
43+
customElements.define('my-element', defineSSRCustomElement(def))
44+
customElements.define(
45+
'my-element-async',
46+
defineSSRCustomElement(
47+
defineAsyncComponent(
48+
() =>
49+
new Promise(r => {
50+
;(window as any).resolve = () => r(def)
51+
}),
52+
),
53+
),
54+
)
55+
})
56+
1357
function getColor() {
1458
return page().evaluate(() => {
1559
return [
@@ -33,3 +77,55 @@ test('ssr custom element hydration', async () => {
3377
await assertInteraction('my-element')
3478
await assertInteraction('my-element-async')
3579
})
80+
81+
// #11641
82+
test('pass key to custom element', async () => {
83+
const messages: string[] = []
84+
page().on('console', e => messages.push(e.text()))
85+
86+
await setContent(
87+
`<!--[--><my-element str="1"><template shadowrootmode="open"><div>1</div></template></my-element><!--]-->`,
88+
)
89+
await page().evaluate(() => {
90+
const {
91+
h,
92+
ref,
93+
defineSSRCustomElement,
94+
onBeforeUnmount,
95+
onMounted,
96+
createSSRApp,
97+
renderList,
98+
} = (window as any).Vue
99+
100+
const MyElement = defineSSRCustomElement({
101+
props: {
102+
str: String,
103+
},
104+
setup(props: any) {
105+
onMounted(() => {
106+
console.log('child mounted')
107+
})
108+
onBeforeUnmount(() => {
109+
console.log('child unmount')
110+
})
111+
return () => h('div', props.str)
112+
},
113+
})
114+
customElements.define('my-element', MyElement)
115+
116+
createSSRApp({
117+
setup() {
118+
const arr = ref(['1'])
119+
// pass key to custom element
120+
return () =>
121+
renderList(arr.value, (i: string) =>
122+
h('my-element', { key: i, str: i }, null),
123+
)
124+
},
125+
}).mount('#app')
126+
})
127+
128+
expect(messages.includes('child mounted')).toBe(true)
129+
expect(messages.includes('child unmount')).toBe(false)
130+
expect(await text('my-element >>> div')).toBe('1')
131+
})

0 commit comments

Comments
 (0)