From 49f6378eeef0536ebbcf5abd873f9fbd538e572f Mon Sep 17 00:00:00 2001 From: HcySunYang Date: Sun, 3 Jan 2021 16:06:29 +0800 Subject: [PATCH 1/4] fix(runtime-dom): support mounting app to svg container --- packages/runtime-core/src/apiCreateApp.ts | 13 +++++++++---- packages/runtime-core/src/renderer.ts | 7 ++++--- packages/runtime-dom/__tests__/createApp.spec.ts | 15 +++++++++++++++ packages/runtime-dom/src/index.ts | 4 ++-- 4 files changed, 30 insertions(+), 9 deletions(-) create mode 100644 packages/runtime-dom/__tests__/createApp.spec.ts diff --git a/packages/runtime-core/src/apiCreateApp.ts b/packages/runtime-core/src/apiCreateApp.ts index bcb2fec1c32..1459ec8135a 100644 --- a/packages/runtime-core/src/apiCreateApp.ts +++ b/packages/runtime-core/src/apiCreateApp.ts @@ -27,7 +27,8 @@ export interface App { directive(name: string, directive: Directive): this mount( rootContainer: HostElement | string, - isHydrate?: boolean + isHydrate?: boolean, + isSVG?: boolean ): ComponentPublicInstance unmount(rootContainer: HostElement | string): void provide(key: InjectionKey | string, value: T): this @@ -224,7 +225,11 @@ export function createAppAPI( return app }, - mount(rootContainer: HostElement, isHydrate?: boolean): any { + mount( + rootContainer: HostElement, + isHydrate?: boolean, + isSVG = false + ): any { if (!isMounted) { const vnode = createVNode( rootComponent as ConcreteComponent, @@ -237,14 +242,14 @@ export function createAppAPI( // HMR root reload if (__DEV__) { context.reload = () => { - render(cloneVNode(vnode), rootContainer) + render(cloneVNode(vnode), rootContainer, isSVG) } } if (isHydrate && hydrate) { hydrate(vnode as VNode, rootContainer as any) } else { - render(vnode, rootContainer) + render(vnode, rootContainer, isSVG) } isMounted = true app._container = rootContainer diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index da86040d0a1..e1a7a7289df 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -87,7 +87,8 @@ export interface HydrationRenderer extends Renderer { export type RootRenderFunction = ( vnode: VNode | null, - container: HostElement + container: HostElement, + isSVG?: boolean ) => void export interface RendererOptions< @@ -2186,13 +2187,13 @@ function baseCreateRenderer( return hostNextSibling((vnode.anchor || vnode.el)!) } - const render: RootRenderFunction = (vnode, container) => { + const render: RootRenderFunction = (vnode, container, isSVG = false) => { if (vnode == null) { if (container._vnode) { unmount(container._vnode, null, null, true) } } else { - patch(container._vnode || null, vnode, container) + patch(container._vnode || null, vnode, container, null, null, null, isSVG) } flushPostFlushCbs() container._vnode = vnode diff --git a/packages/runtime-dom/__tests__/createApp.spec.ts b/packages/runtime-dom/__tests__/createApp.spec.ts new file mode 100644 index 00000000000..00c9282e1b3 --- /dev/null +++ b/packages/runtime-dom/__tests__/createApp.spec.ts @@ -0,0 +1,15 @@ +import { createApp, h } from '../src' + +describe('createApp for dom', () => { + // #2926 + test('mount to SVG container', () => { + const root = document.createElementNS('http://www.w3.org/2000/svg', 'svg') + createApp({ + render() { + return h('g') + } + }).mount(root) + expect(root.children.length).toBe(1) + expect(root.children[0] instanceof SVGElement).toBe(true) + }) +}) diff --git a/packages/runtime-dom/src/index.ts b/packages/runtime-dom/src/index.ts index 0dfe9fba003..d4bb372df9a 100644 --- a/packages/runtime-dom/src/index.ts +++ b/packages/runtime-dom/src/index.ts @@ -67,7 +67,7 @@ export const createApp = ((...args) => { } // clear content before mounting container.innerHTML = '' - const proxy = mount(container) + const proxy = mount(container, false, container instanceof SVGElement) if (container instanceof Element) { container.removeAttribute('v-cloak') container.setAttribute('data-v-app', '') @@ -89,7 +89,7 @@ export const createSSRApp = ((...args) => { app.mount = (containerOrSelector: Element | ShadowRoot | string): any => { const container = normalizeContainer(containerOrSelector) if (container) { - return mount(container, true) + return mount(container, true, container instanceof Element) } } From c1221bf995676164966687ee18b254e1e5715f65 Mon Sep 17 00:00:00 2001 From: HcySunYang Date: Mon, 4 Jan 2021 19:50:30 +0800 Subject: [PATCH 2/4] fix: remove the default value of isSVG --- packages/runtime-core/src/apiCreateApp.ts | 2 +- packages/runtime-core/src/renderer.ts | 2 +- packages/runtime-dom/src/index.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/runtime-core/src/apiCreateApp.ts b/packages/runtime-core/src/apiCreateApp.ts index 1459ec8135a..a4f713162c1 100644 --- a/packages/runtime-core/src/apiCreateApp.ts +++ b/packages/runtime-core/src/apiCreateApp.ts @@ -228,7 +228,7 @@ export function createAppAPI( mount( rootContainer: HostElement, isHydrate?: boolean, - isSVG = false + isSVG?: boolean ): any { if (!isMounted) { const vnode = createVNode( diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index e1a7a7289df..b15b32ceac6 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -2187,7 +2187,7 @@ function baseCreateRenderer( return hostNextSibling((vnode.anchor || vnode.el)!) } - const render: RootRenderFunction = (vnode, container, isSVG = false) => { + const render: RootRenderFunction = (vnode, container, isSVG) => { if (vnode == null) { if (container._vnode) { unmount(container._vnode, null, null, true) diff --git a/packages/runtime-dom/src/index.ts b/packages/runtime-dom/src/index.ts index d4bb372df9a..d04c74c12e2 100644 --- a/packages/runtime-dom/src/index.ts +++ b/packages/runtime-dom/src/index.ts @@ -89,7 +89,7 @@ export const createSSRApp = ((...args) => { app.mount = (containerOrSelector: Element | ShadowRoot | string): any => { const container = normalizeContainer(containerOrSelector) if (container) { - return mount(container, true, container instanceof Element) + return mount(container, true, container instanceof SVGElement) } } From 445b4b80b187e1ba6abfb20549090d869a64dc13 Mon Sep 17 00:00:00 2001 From: HcySunYang Date: Mon, 4 Jan 2021 20:04:15 +0800 Subject: [PATCH 3/4] test: add test for ssr --- packages/runtime-core/__tests__/hydration.spec.ts | 14 ++++++++++++++ packages/runtime-core/src/apiCreateApp.ts | 3 +-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/runtime-core/__tests__/hydration.spec.ts b/packages/runtime-core/__tests__/hydration.spec.ts index 60978622bc6..4c7aa911bcc 100644 --- a/packages/runtime-core/__tests__/hydration.spec.ts +++ b/packages/runtime-core/__tests__/hydration.spec.ts @@ -617,6 +617,20 @@ describe('SSR hydration', () => { expect(spy).toHaveBeenCalled() }) + test('SVG as a mount container', () => { + const svgContainer = document.createElement('svg') + svgContainer.innerHTML = '' + const app = createSSRApp({ + render: () => h('g') + }) + + expect( + (app.mount(svgContainer).$.subTree as VNode & { + el: Element + }).el instanceof SVGElement + ) + }) + describe('mismatch handling', () => { test('text node', () => { const { container } = mountWithHydration(`foo`, () => 'bar') diff --git a/packages/runtime-core/src/apiCreateApp.ts b/packages/runtime-core/src/apiCreateApp.ts index a4f713162c1..2a1c55e093a 100644 --- a/packages/runtime-core/src/apiCreateApp.ts +++ b/packages/runtime-core/src/apiCreateApp.ts @@ -27,8 +27,7 @@ export interface App { directive(name: string, directive: Directive): this mount( rootContainer: HostElement | string, - isHydrate?: boolean, - isSVG?: boolean + isHydrate?: boolean ): ComponentPublicInstance unmount(rootContainer: HostElement | string): void provide(key: InjectionKey | string, value: T): this From 602d76c373d44d624142a2be02e7a7793c9aa73e Mon Sep 17 00:00:00 2001 From: HcySunYang Date: Mon, 4 Jan 2021 20:18:00 +0800 Subject: [PATCH 4/4] fix: types --- packages/runtime-core/src/apiCreateApp.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/runtime-core/src/apiCreateApp.ts b/packages/runtime-core/src/apiCreateApp.ts index 2a1c55e093a..a4f713162c1 100644 --- a/packages/runtime-core/src/apiCreateApp.ts +++ b/packages/runtime-core/src/apiCreateApp.ts @@ -27,7 +27,8 @@ export interface App { directive(name: string, directive: Directive): this mount( rootContainer: HostElement | string, - isHydrate?: boolean + isHydrate?: boolean, + isSVG?: boolean ): ComponentPublicInstance unmount(rootContainer: HostElement | string): void provide(key: InjectionKey | string, value: T): this