Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(runtime-dom): support mounting app to svg container #2929

Merged
merged 4 commits into from
Mar 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions packages/runtime-core/__tests__/hydration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,20 @@ describe('SSR hydration', () => {
expect(spy).toHaveBeenCalled()
})

test('SVG as a mount container', () => {
const svgContainer = document.createElement('svg')
svgContainer.innerHTML = '<g></g>'
const app = createSSRApp({
render: () => h('g')
})

expect(
(app.mount(svgContainer).$.subTree as VNode<Node, Element> & {
el: Element
}).el instanceof SVGElement
)
})

describe('mismatch handling', () => {
test('text node', () => {
const { container } = mountWithHydration(`foo`, () => 'bar')
Expand Down
13 changes: 9 additions & 4 deletions packages/runtime-core/src/apiCreateApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ export interface App<HostElement = any> {
directive(name: string, directive: Directive): this
mount(
rootContainer: HostElement | string,
isHydrate?: boolean
isHydrate?: boolean,
isSVG?: boolean
): ComponentPublicInstance
unmount(rootContainer: HostElement | string): void
provide<T>(key: InjectionKey<T> | string, value: T): this
Expand Down Expand Up @@ -224,7 +225,11 @@ export function createAppAPI<HostElement>(
return app
},

mount(rootContainer: HostElement, isHydrate?: boolean): any {
mount(
rootContainer: HostElement,
isHydrate?: boolean,
isSVG?: boolean
): any {
if (!isMounted) {
const vnode = createVNode(
rootComponent as ConcreteComponent,
Expand All @@ -237,14 +242,14 @@ export function createAppAPI<HostElement>(
// HMR root reload
if (__DEV__) {
context.reload = () => {
render(cloneVNode(vnode), rootContainer)
render(cloneVNode(vnode), rootContainer, isSVG)
}
}

if (isHydrate && hydrate) {
hydrate(vnode as VNode<Node, Element>, rootContainer as any)
} else {
render(vnode, rootContainer)
render(vnode, rootContainer, isSVG)
}
isMounted = true
app._container = rootContainer
Expand Down
7 changes: 4 additions & 3 deletions packages/runtime-core/src/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ export interface HydrationRenderer extends Renderer<Element> {

export type RootRenderFunction<HostElement = RendererElement> = (
vnode: VNode | null,
container: HostElement
container: HostElement,
isSVG?: boolean
) => void

export interface RendererOptions<
Expand Down Expand Up @@ -2186,13 +2187,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
Expand Down
15 changes: 15 additions & 0 deletions packages/runtime-dom/__tests__/createApp.spec.ts
Original file line number Diff line number Diff line change
@@ -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)
})
})
4 changes: 2 additions & 2 deletions packages/runtime-dom/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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', '')
Expand All @@ -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 SVGElement)
}
}

Expand Down