|
|
@@ -10,14 +10,28 @@ import {
|
|
|
markRaw,
|
|
|
nextTick,
|
|
|
nodeOps,
|
|
|
+ onMounted,
|
|
|
h as originalH,
|
|
|
ref,
|
|
|
render,
|
|
|
+ serialize,
|
|
|
serializeInner,
|
|
|
withDirectives,
|
|
|
} from '@vue/runtime-test'
|
|
|
-import { Fragment, createCommentVNode, createVNode } from '../../src/vnode'
|
|
|
+import {
|
|
|
+ Fragment,
|
|
|
+ createBlock,
|
|
|
+ createCommentVNode,
|
|
|
+ createTextVNode,
|
|
|
+ createVNode,
|
|
|
+ openBlock,
|
|
|
+} from '../../src/vnode'
|
|
|
+import { toDisplayString } from '@vue/shared'
|
|
|
import { compile, createApp as createDOMApp, render as domRender } from 'vue'
|
|
|
+import type { HMRRuntime } from '../../src/hmr'
|
|
|
+
|
|
|
+declare var __VUE_HMR_RUNTIME__: HMRRuntime
|
|
|
+const { rerender, createRecord } = __VUE_HMR_RUNTIME__
|
|
|
|
|
|
describe('renderer: teleport', () => {
|
|
|
describe('eager mode', () => {
|
|
|
@@ -243,6 +257,39 @@ describe('renderer: teleport', () => {
|
|
|
expect(serializeInner(target)).toBe(`teleported`)
|
|
|
})
|
|
|
|
|
|
+ test('should traverse comment node after updating in optimize mode', async () => {
|
|
|
+ const target = nodeOps.createElement('div')
|
|
|
+ const root = nodeOps.createElement('div')
|
|
|
+ const count = ref(0)
|
|
|
+ let teleport
|
|
|
+
|
|
|
+ __DEV__ = false
|
|
|
+ render(
|
|
|
+ h(() => {
|
|
|
+ teleport =
|
|
|
+ (openBlock(),
|
|
|
+ createBlock(Teleport, { to: target }, [
|
|
|
+ createCommentVNode('comment in teleport'),
|
|
|
+ ]))
|
|
|
+ return h('div', null, [
|
|
|
+ createTextVNode(toDisplayString(count.value)),
|
|
|
+ teleport,
|
|
|
+ ])
|
|
|
+ }),
|
|
|
+ root,
|
|
|
+ )
|
|
|
+ const commentNode = teleport!.children[0].el
|
|
|
+ expect(serializeInner(root)).toBe(`<div>0</div>`)
|
|
|
+ expect(serializeInner(target)).toBe(`<!--comment in teleport-->`)
|
|
|
+ expect(serialize(commentNode)).toBe(`<!--comment in teleport-->`)
|
|
|
+
|
|
|
+ count.value = 1
|
|
|
+ await nextTick()
|
|
|
+ __DEV__ = true
|
|
|
+ expect(serializeInner(root)).toBe(`<div>1</div>`)
|
|
|
+ expect(teleport!.children[0].el).toBe(commentNode)
|
|
|
+ })
|
|
|
+
|
|
|
test('should remove children when unmounted', () => {
|
|
|
const target = nodeOps.createElement('div')
|
|
|
const root = nodeOps.createElement('div')
|
|
|
@@ -269,6 +316,34 @@ describe('renderer: teleport', () => {
|
|
|
testUnmount({ to: null, disabled: true })
|
|
|
})
|
|
|
|
|
|
+ // #10747
|
|
|
+ test('should unmount correctly when using top level comment in teleport', async () => {
|
|
|
+ const target = nodeOps.createElement('div')
|
|
|
+ const root = nodeOps.createElement('div')
|
|
|
+ const count = ref(0)
|
|
|
+
|
|
|
+ __DEV__ = false
|
|
|
+ render(
|
|
|
+ h(() => {
|
|
|
+ return h('div', null, [
|
|
|
+ createTextVNode(toDisplayString(count.value)),
|
|
|
+ (openBlock(),
|
|
|
+ createBlock(Teleport, { to: target }, [
|
|
|
+ createCommentVNode('comment in teleport'),
|
|
|
+ ])),
|
|
|
+ ])
|
|
|
+ }),
|
|
|
+ root,
|
|
|
+ )
|
|
|
+
|
|
|
+ count.value = 1
|
|
|
+
|
|
|
+ await nextTick()
|
|
|
+ __DEV__ = true
|
|
|
+ render(null, root)
|
|
|
+ expect(root.children.length).toBe(0)
|
|
|
+ })
|
|
|
+
|
|
|
test('component with multi roots should be removed when unmounted', () => {
|
|
|
const target = nodeOps.createElement('div')
|
|
|
const root = nodeOps.createElement('div')
|
|
|
@@ -741,4 +816,56 @@ describe('renderer: teleport', () => {
|
|
|
expect(tRefInMounted).toBe(target.children[1])
|
|
|
})
|
|
|
}
|
|
|
+
|
|
|
+ test('handle update and hmr rerender', async () => {
|
|
|
+ const target = document.createElement('div')
|
|
|
+ const root = document.createElement('div')
|
|
|
+
|
|
|
+ const Comp = {
|
|
|
+ setup() {
|
|
|
+ const cls = ref('foo')
|
|
|
+ onMounted(() => {
|
|
|
+ // trigger update
|
|
|
+ cls.value = 'bar'
|
|
|
+ })
|
|
|
+ return { cls, target }
|
|
|
+ },
|
|
|
+ template: `
|
|
|
+ <Teleport :to="target">
|
|
|
+ <div :class="cls">
|
|
|
+ <div>
|
|
|
+ <slot></slot>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </Teleport>
|
|
|
+ `,
|
|
|
+ }
|
|
|
+
|
|
|
+ const appId = 'test-app-id'
|
|
|
+ const App = {
|
|
|
+ __hmrId: appId,
|
|
|
+ components: { Comp },
|
|
|
+ render() {
|
|
|
+ return originalH(Comp, null, { default: () => originalH('div', 'foo') })
|
|
|
+ },
|
|
|
+ }
|
|
|
+ createRecord(appId, App)
|
|
|
+
|
|
|
+ domRender(originalH(App), root)
|
|
|
+ expect(target.innerHTML).toBe(
|
|
|
+ '<div class="foo"><div><div>foo</div></div></div>',
|
|
|
+ )
|
|
|
+ await nextTick()
|
|
|
+ expect(target.innerHTML).toBe(
|
|
|
+ '<div class="bar"><div><div>foo</div></div></div>',
|
|
|
+ )
|
|
|
+
|
|
|
+ rerender(appId, () =>
|
|
|
+ originalH(Comp, null, { default: () => originalH('div', 'bar') }),
|
|
|
+ )
|
|
|
+ await nextTick()
|
|
|
+ expect(target.innerHTML).toBe(
|
|
|
+ '<div class="bar"><div><div>bar</div></div></div>',
|
|
|
+ )
|
|
|
+ })
|
|
|
})
|