From 8ffcde2836baa41d279d9cc079f139a2e31cf6be Mon Sep 17 00:00:00 2001 From: HcySunYang Date: Tue, 2 Mar 2021 00:51:32 +0800 Subject: [PATCH] fix(runtime-dom): support mounting app to svg container (#2929) fix #2926 --- packages/runtime-core/__tests__/hydration.spec.ts | 14 ++++++++++++++ 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 ++-- 5 files changed, 44 insertions(+), 9 deletions(-) create mode 100644 packages/runtime-dom/__tests__/createApp.spec.ts 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 869e5809f05..92cce58435b 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(): 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?: boolean + ): 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 bc1d154527b..b7973284f70 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -93,7 +93,8 @@ export interface HydrationRenderer extends Renderer { export type RootRenderFunction = ( vnode: VNode | null, - container: HostElement + container: HostElement, + isSVG?: boolean ) => void export interface RendererOptions< @@ -2202,13 +2203,13 @@ function baseCreateRenderer( return hostNextSibling((vnode.anchor || vnode.el)!) } - const render: RootRenderFunction = (vnode, container) => { + const render: RootRenderFunction = (vnode, container, isSVG) => { 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 8a66ec994c9..773470621fe 100644 --- a/packages/runtime-dom/src/index.ts +++ b/packages/runtime-dom/src/index.ts @@ -69,7 +69,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', '') @@ -92,7 +92,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 SVGElement) } }