mirror of https://github.com/vuejs/core.git
fix(custom-element): handle keys set on custom elements (#11655)
close #11641
This commit is contained in:
parent
1d988b5b99
commit
f1d1831f07
|
@ -487,6 +487,10 @@ export class VueElement
|
|||
delete this._props[key]
|
||||
} else {
|
||||
this._props[key] = val
|
||||
// support set key on ceVNode
|
||||
if (key === 'key' && this._app) {
|
||||
this._app._ceVNode!.key = val
|
||||
}
|
||||
}
|
||||
if (shouldUpdate && this._instance) {
|
||||
this._update()
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
<script src="../../dist/vue.global.js"></script>
|
||||
|
||||
<my-element
|
||||
><template shadowrootmode="open"><button>1</button></template></my-element
|
||||
>
|
||||
<my-element-async
|
||||
><template shadowrootmode="open"
|
||||
><button>1</button></template
|
||||
></my-element-async
|
||||
>
|
||||
|
||||
<script>
|
||||
const {
|
||||
h,
|
||||
ref,
|
||||
defineSSRCustomElement,
|
||||
defineAsyncComponent,
|
||||
onMounted,
|
||||
useHost,
|
||||
} = Vue
|
||||
|
||||
const def = {
|
||||
setup() {
|
||||
const count = ref(1)
|
||||
const el = useHost()
|
||||
onMounted(() => (el.style.border = '1px solid red'))
|
||||
|
||||
return () => h('button', { onClick: () => count.value++ }, count.value)
|
||||
},
|
||||
}
|
||||
|
||||
customElements.define('my-element', defineSSRCustomElement(def))
|
||||
customElements.define(
|
||||
'my-element-async',
|
||||
defineSSRCustomElement(
|
||||
defineAsyncComponent(
|
||||
() =>
|
||||
new Promise(r => {
|
||||
window.resolve = () => r(def)
|
||||
}),
|
||||
),
|
||||
),
|
||||
)
|
||||
</script>
|
|
@ -3,13 +3,57 @@ import { setupPuppeteer } from './e2eUtils'
|
|||
|
||||
const { page, click, text } = setupPuppeteer()
|
||||
|
||||
beforeEach(async () => {
|
||||
await page().addScriptTag({
|
||||
path: path.resolve(__dirname, '../../dist/vue.global.js'),
|
||||
})
|
||||
})
|
||||
|
||||
async function setContent(html: string) {
|
||||
await page().setContent(`<div id="app">${html}</div>`)
|
||||
}
|
||||
|
||||
// this must be tested in actual Chrome because jsdom does not support
|
||||
// declarative shadow DOM
|
||||
test('ssr custom element hydration', async () => {
|
||||
await page().goto(
|
||||
`file://${path.resolve(__dirname, './ssr-custom-element.html')}`,
|
||||
await setContent(
|
||||
`<my-element><template shadowrootmode="open"><button>1</button></template></my-element><my-element-async><template shadowrootmode="open"><button>1</button></template></my-element-async>`,
|
||||
)
|
||||
|
||||
await page().evaluate(() => {
|
||||
const {
|
||||
h,
|
||||
ref,
|
||||
defineSSRCustomElement,
|
||||
defineAsyncComponent,
|
||||
onMounted,
|
||||
useHost,
|
||||
} = (window as any).Vue
|
||||
|
||||
const def = {
|
||||
setup() {
|
||||
const count = ref(1)
|
||||
const el = useHost()
|
||||
onMounted(() => (el.style.border = '1px solid red'))
|
||||
|
||||
return () => h('button', { onClick: () => count.value++ }, count.value)
|
||||
},
|
||||
}
|
||||
|
||||
customElements.define('my-element', defineSSRCustomElement(def))
|
||||
customElements.define(
|
||||
'my-element-async',
|
||||
defineSSRCustomElement(
|
||||
defineAsyncComponent(
|
||||
() =>
|
||||
new Promise(r => {
|
||||
;(window as any).resolve = () => r(def)
|
||||
}),
|
||||
),
|
||||
),
|
||||
)
|
||||
})
|
||||
|
||||
function getColor() {
|
||||
return page().evaluate(() => {
|
||||
return [
|
||||
|
@ -33,3 +77,55 @@ test('ssr custom element hydration', async () => {
|
|||
await assertInteraction('my-element')
|
||||
await assertInteraction('my-element-async')
|
||||
})
|
||||
|
||||
// #11641
|
||||
test('pass key to custom element', async () => {
|
||||
const messages: string[] = []
|
||||
page().on('console', e => messages.push(e.text()))
|
||||
|
||||
await setContent(
|
||||
`<!--[--><my-element str="1"><template shadowrootmode="open"><div>1</div></template></my-element><!--]-->`,
|
||||
)
|
||||
await page().evaluate(() => {
|
||||
const {
|
||||
h,
|
||||
ref,
|
||||
defineSSRCustomElement,
|
||||
onBeforeUnmount,
|
||||
onMounted,
|
||||
createSSRApp,
|
||||
renderList,
|
||||
} = (window as any).Vue
|
||||
|
||||
const MyElement = defineSSRCustomElement({
|
||||
props: {
|
||||
str: String,
|
||||
},
|
||||
setup(props: any) {
|
||||
onMounted(() => {
|
||||
console.log('child mounted')
|
||||
})
|
||||
onBeforeUnmount(() => {
|
||||
console.log('child unmount')
|
||||
})
|
||||
return () => h('div', props.str)
|
||||
},
|
||||
})
|
||||
customElements.define('my-element', MyElement)
|
||||
|
||||
createSSRApp({
|
||||
setup() {
|
||||
const arr = ref(['1'])
|
||||
// pass key to custom element
|
||||
return () =>
|
||||
renderList(arr.value, (i: string) =>
|
||||
h('my-element', { key: i, str: i }, null),
|
||||
)
|
||||
},
|
||||
}).mount('#app')
|
||||
})
|
||||
|
||||
expect(messages.includes('child mounted')).toBe(true)
|
||||
expect(messages.includes('child unmount')).toBe(false)
|
||||
expect(await text('my-element >>> div')).toBe('1')
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue