mirror of https://github.com/vuejs/core.git
parent
bc7698dbfe
commit
d42b6ba3f5
|
@ -1,12 +1,12 @@
|
||||||
import { ParserOptions, NodeTypes, Namespaces } from '@vue/compiler-core'
|
import { ParserOptions, NodeTypes, Namespaces } from '@vue/compiler-core'
|
||||||
import { isVoidTag, isHTMLTag, isSVGTag } from '@vue/shared'
|
import { isVoidTag, isHTMLTag, isSVGTag, isMathMLTag } from '@vue/shared'
|
||||||
import { TRANSITION, TRANSITION_GROUP } from './runtimeHelpers'
|
import { TRANSITION, TRANSITION_GROUP } from './runtimeHelpers'
|
||||||
import { decodeHtmlBrowser } from './decodeHtmlBrowser'
|
import { decodeHtmlBrowser } from './decodeHtmlBrowser'
|
||||||
|
|
||||||
export const parserOptions: ParserOptions = {
|
export const parserOptions: ParserOptions = {
|
||||||
parseMode: 'html',
|
parseMode: 'html',
|
||||||
isVoidTag,
|
isVoidTag,
|
||||||
isNativeTag: tag => isHTMLTag(tag) || isSVGTag(tag),
|
isNativeTag: tag => isHTMLTag(tag) || isSVGTag(tag) || isMathMLTag(tag),
|
||||||
isPreTag: tag => tag === 'pre',
|
isPreTag: tag => tag === 'pre',
|
||||||
decodeEntities: __BROWSER__ ? decodeHtmlBrowser : undefined,
|
decodeEntities: __BROWSER__ ? decodeHtmlBrowser : undefined,
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ import {
|
||||||
ComponentPublicInstance
|
ComponentPublicInstance
|
||||||
} from './componentPublicInstance'
|
} from './componentPublicInstance'
|
||||||
import { Directive, validateDirectiveName } from './directives'
|
import { Directive, validateDirectiveName } from './directives'
|
||||||
import { RootRenderFunction } from './renderer'
|
import { ElementNamespace, RootRenderFunction } from './renderer'
|
||||||
import { InjectionKey } from './apiInject'
|
import { InjectionKey } from './apiInject'
|
||||||
import { warn } from './warning'
|
import { warn } from './warning'
|
||||||
import { createVNode, cloneVNode, VNode } from './vnode'
|
import { createVNode, cloneVNode, VNode } from './vnode'
|
||||||
|
@ -47,7 +47,7 @@ export interface App<HostElement = any> {
|
||||||
mount(
|
mount(
|
||||||
rootContainer: HostElement | string,
|
rootContainer: HostElement | string,
|
||||||
isHydrate?: boolean,
|
isHydrate?: boolean,
|
||||||
isSVG?: boolean
|
namespace?: boolean | ElementNamespace
|
||||||
): ComponentPublicInstance
|
): ComponentPublicInstance
|
||||||
unmount(): void
|
unmount(): void
|
||||||
provide<T>(key: InjectionKey<T> | string, value: T): this
|
provide<T>(key: InjectionKey<T> | string, value: T): this
|
||||||
|
@ -297,7 +297,7 @@ export function createAppAPI<HostElement>(
|
||||||
mount(
|
mount(
|
||||||
rootContainer: HostElement,
|
rootContainer: HostElement,
|
||||||
isHydrate?: boolean,
|
isHydrate?: boolean,
|
||||||
isSVG?: boolean
|
namespace?: boolean | ElementNamespace
|
||||||
): any {
|
): any {
|
||||||
if (!isMounted) {
|
if (!isMounted) {
|
||||||
// #5571
|
// #5571
|
||||||
|
@ -313,17 +313,29 @@ export function createAppAPI<HostElement>(
|
||||||
// this will be set on the root instance on initial mount.
|
// this will be set on the root instance on initial mount.
|
||||||
vnode.appContext = context
|
vnode.appContext = context
|
||||||
|
|
||||||
|
if (namespace === true) {
|
||||||
|
namespace = 'svg'
|
||||||
|
} else if (namespace === false) {
|
||||||
|
namespace = undefined
|
||||||
|
}
|
||||||
|
|
||||||
// HMR root reload
|
// HMR root reload
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
context.reload = () => {
|
context.reload = () => {
|
||||||
render(cloneVNode(vnode), rootContainer, isSVG)
|
// casting to ElementNamespace because TS doesn't guarantee type narrowing
|
||||||
|
// over function boundaries
|
||||||
|
render(
|
||||||
|
cloneVNode(vnode),
|
||||||
|
rootContainer,
|
||||||
|
namespace as ElementNamespace
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isHydrate && hydrate) {
|
if (isHydrate && hydrate) {
|
||||||
hydrate(vnode as VNode<Node, Element>, rootContainer as any)
|
hydrate(vnode as VNode<Node, Element>, rootContainer as any)
|
||||||
} else {
|
} else {
|
||||||
render(vnode, rootContainer, isSVG)
|
render(vnode, rootContainer, namespace)
|
||||||
}
|
}
|
||||||
isMounted = true
|
isMounted = true
|
||||||
app._container = rootContainer
|
app._container = rootContainer
|
||||||
|
|
|
@ -17,7 +17,7 @@ import {
|
||||||
} from '@vue/shared'
|
} from '@vue/shared'
|
||||||
import { warn } from '../warning'
|
import { warn } from '../warning'
|
||||||
import { cloneVNode, createVNode } from '../vnode'
|
import { cloneVNode, createVNode } from '../vnode'
|
||||||
import { RootRenderFunction } from '../renderer'
|
import { ElementNamespace, RootRenderFunction } from '../renderer'
|
||||||
import {
|
import {
|
||||||
App,
|
App,
|
||||||
AppConfig,
|
AppConfig,
|
||||||
|
@ -503,7 +503,13 @@ function installCompatMount(
|
||||||
container = selectorOrEl || document.createElement('div')
|
container = selectorOrEl || document.createElement('div')
|
||||||
}
|
}
|
||||||
|
|
||||||
const isSVG = container instanceof SVGElement
|
let namespace: ElementNamespace
|
||||||
|
if (container instanceof SVGElement) namespace = 'svg'
|
||||||
|
else if (
|
||||||
|
typeof MathMLElement === 'function' &&
|
||||||
|
container instanceof MathMLElement
|
||||||
|
)
|
||||||
|
namespace = 'mathml'
|
||||||
|
|
||||||
// HMR root reload
|
// HMR root reload
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
|
@ -511,7 +517,7 @@ function installCompatMount(
|
||||||
const cloned = cloneVNode(vnode)
|
const cloned = cloneVNode(vnode)
|
||||||
// compat mode will use instance if not reset to null
|
// compat mode will use instance if not reset to null
|
||||||
cloned.component = null
|
cloned.component = null
|
||||||
render(cloned, container, isSVG)
|
render(cloned, container, namespace)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -538,7 +544,7 @@ function installCompatMount(
|
||||||
container.innerHTML = ''
|
container.innerHTML = ''
|
||||||
|
|
||||||
// TODO hydration
|
// TODO hydration
|
||||||
render(vnode, container, isSVG)
|
render(vnode, container, namespace)
|
||||||
|
|
||||||
if (container instanceof Element) {
|
if (container instanceof Element) {
|
||||||
container.removeAttribute('v-cloak')
|
container.removeAttribute('v-cloak')
|
||||||
|
|
|
@ -37,7 +37,8 @@ import {
|
||||||
queuePostRenderEffect,
|
queuePostRenderEffect,
|
||||||
MoveType,
|
MoveType,
|
||||||
RendererElement,
|
RendererElement,
|
||||||
RendererNode
|
RendererNode,
|
||||||
|
ElementNamespace
|
||||||
} from '../renderer'
|
} from '../renderer'
|
||||||
import { setTransitionHooks } from './BaseTransition'
|
import { setTransitionHooks } from './BaseTransition'
|
||||||
import { ComponentRenderContext } from '../componentPublicInstance'
|
import { ComponentRenderContext } from '../componentPublicInstance'
|
||||||
|
@ -64,7 +65,7 @@ export interface KeepAliveContext extends ComponentRenderContext {
|
||||||
vnode: VNode,
|
vnode: VNode,
|
||||||
container: RendererElement,
|
container: RendererElement,
|
||||||
anchor: RendererNode | null,
|
anchor: RendererNode | null,
|
||||||
isSVG: boolean,
|
namespace: ElementNamespace,
|
||||||
optimized: boolean
|
optimized: boolean
|
||||||
) => void
|
) => void
|
||||||
deactivate: (vnode: VNode) => void
|
deactivate: (vnode: VNode) => void
|
||||||
|
@ -125,7 +126,13 @@ const KeepAliveImpl: ComponentOptions = {
|
||||||
} = sharedContext
|
} = sharedContext
|
||||||
const storageContainer = createElement('div')
|
const storageContainer = createElement('div')
|
||||||
|
|
||||||
sharedContext.activate = (vnode, container, anchor, isSVG, optimized) => {
|
sharedContext.activate = (
|
||||||
|
vnode,
|
||||||
|
container,
|
||||||
|
anchor,
|
||||||
|
namespace,
|
||||||
|
optimized
|
||||||
|
) => {
|
||||||
const instance = vnode.component!
|
const instance = vnode.component!
|
||||||
move(vnode, container, anchor, MoveType.ENTER, parentSuspense)
|
move(vnode, container, anchor, MoveType.ENTER, parentSuspense)
|
||||||
// in case props have changed
|
// in case props have changed
|
||||||
|
@ -136,7 +143,7 @@ const KeepAliveImpl: ComponentOptions = {
|
||||||
anchor,
|
anchor,
|
||||||
instance,
|
instance,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG,
|
namespace,
|
||||||
vnode.slotScopeIds,
|
vnode.slotScopeIds,
|
||||||
optimized
|
optimized
|
||||||
)
|
)
|
||||||
|
|
|
@ -18,7 +18,8 @@ import {
|
||||||
MoveType,
|
MoveType,
|
||||||
SetupRenderEffectFn,
|
SetupRenderEffectFn,
|
||||||
RendererNode,
|
RendererNode,
|
||||||
RendererElement
|
RendererElement,
|
||||||
|
ElementNamespace
|
||||||
} from '../renderer'
|
} from '../renderer'
|
||||||
import { queuePostFlushCb } from '../scheduler'
|
import { queuePostFlushCb } from '../scheduler'
|
||||||
import { filterSingleRoot, updateHOCHostEl } from '../componentRenderUtils'
|
import { filterSingleRoot, updateHOCHostEl } from '../componentRenderUtils'
|
||||||
|
@ -63,7 +64,7 @@ export const SuspenseImpl = {
|
||||||
anchor: RendererNode | null,
|
anchor: RendererNode | null,
|
||||||
parentComponent: ComponentInternalInstance | null,
|
parentComponent: ComponentInternalInstance | null,
|
||||||
parentSuspense: SuspenseBoundary | null,
|
parentSuspense: SuspenseBoundary | null,
|
||||||
isSVG: boolean,
|
namespace: ElementNamespace,
|
||||||
slotScopeIds: string[] | null,
|
slotScopeIds: string[] | null,
|
||||||
optimized: boolean,
|
optimized: boolean,
|
||||||
// platform-specific impl passed from renderer
|
// platform-specific impl passed from renderer
|
||||||
|
@ -76,7 +77,7 @@ export const SuspenseImpl = {
|
||||||
anchor,
|
anchor,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
optimized,
|
optimized,
|
||||||
rendererInternals
|
rendererInternals
|
||||||
|
@ -88,7 +89,7 @@ export const SuspenseImpl = {
|
||||||
container,
|
container,
|
||||||
anchor,
|
anchor,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
optimized,
|
optimized,
|
||||||
rendererInternals
|
rendererInternals
|
||||||
|
@ -130,7 +131,7 @@ function mountSuspense(
|
||||||
anchor: RendererNode | null,
|
anchor: RendererNode | null,
|
||||||
parentComponent: ComponentInternalInstance | null,
|
parentComponent: ComponentInternalInstance | null,
|
||||||
parentSuspense: SuspenseBoundary | null,
|
parentSuspense: SuspenseBoundary | null,
|
||||||
isSVG: boolean,
|
namespace: ElementNamespace,
|
||||||
slotScopeIds: string[] | null,
|
slotScopeIds: string[] | null,
|
||||||
optimized: boolean,
|
optimized: boolean,
|
||||||
rendererInternals: RendererInternals
|
rendererInternals: RendererInternals
|
||||||
|
@ -147,7 +148,7 @@ function mountSuspense(
|
||||||
container,
|
container,
|
||||||
hiddenContainer,
|
hiddenContainer,
|
||||||
anchor,
|
anchor,
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
optimized,
|
optimized,
|
||||||
rendererInternals
|
rendererInternals
|
||||||
|
@ -161,7 +162,7 @@ function mountSuspense(
|
||||||
null,
|
null,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
suspense,
|
suspense,
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds
|
slotScopeIds
|
||||||
)
|
)
|
||||||
// now check if we have encountered any async deps
|
// now check if we have encountered any async deps
|
||||||
|
@ -179,7 +180,7 @@ function mountSuspense(
|
||||||
anchor,
|
anchor,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
null, // fallback tree will not have suspense context
|
null, // fallback tree will not have suspense context
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds
|
slotScopeIds
|
||||||
)
|
)
|
||||||
setActiveBranch(suspense, vnode.ssFallback!)
|
setActiveBranch(suspense, vnode.ssFallback!)
|
||||||
|
@ -195,7 +196,7 @@ function patchSuspense(
|
||||||
container: RendererElement,
|
container: RendererElement,
|
||||||
anchor: RendererNode | null,
|
anchor: RendererNode | null,
|
||||||
parentComponent: ComponentInternalInstance | null,
|
parentComponent: ComponentInternalInstance | null,
|
||||||
isSVG: boolean,
|
namespace: ElementNamespace,
|
||||||
slotScopeIds: string[] | null,
|
slotScopeIds: string[] | null,
|
||||||
optimized: boolean,
|
optimized: boolean,
|
||||||
{ p: patch, um: unmount, o: { createElement } }: RendererInternals
|
{ p: patch, um: unmount, o: { createElement } }: RendererInternals
|
||||||
|
@ -218,7 +219,7 @@ function patchSuspense(
|
||||||
null,
|
null,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
suspense,
|
suspense,
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
optimized
|
optimized
|
||||||
)
|
)
|
||||||
|
@ -232,7 +233,7 @@ function patchSuspense(
|
||||||
anchor,
|
anchor,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
null, // fallback tree will not have suspense context
|
null, // fallback tree will not have suspense context
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
optimized
|
optimized
|
||||||
)
|
)
|
||||||
|
@ -267,7 +268,7 @@ function patchSuspense(
|
||||||
null,
|
null,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
suspense,
|
suspense,
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
optimized
|
optimized
|
||||||
)
|
)
|
||||||
|
@ -281,7 +282,7 @@ function patchSuspense(
|
||||||
anchor,
|
anchor,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
null, // fallback tree will not have suspense context
|
null, // fallback tree will not have suspense context
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
optimized
|
optimized
|
||||||
)
|
)
|
||||||
|
@ -296,7 +297,7 @@ function patchSuspense(
|
||||||
anchor,
|
anchor,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
suspense,
|
suspense,
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
optimized
|
optimized
|
||||||
)
|
)
|
||||||
|
@ -311,7 +312,7 @@ function patchSuspense(
|
||||||
null,
|
null,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
suspense,
|
suspense,
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
optimized
|
optimized
|
||||||
)
|
)
|
||||||
|
@ -330,7 +331,7 @@ function patchSuspense(
|
||||||
anchor,
|
anchor,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
suspense,
|
suspense,
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
optimized
|
optimized
|
||||||
)
|
)
|
||||||
|
@ -349,7 +350,7 @@ function patchSuspense(
|
||||||
null,
|
null,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
suspense,
|
suspense,
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
optimized
|
optimized
|
||||||
)
|
)
|
||||||
|
@ -376,7 +377,7 @@ export interface SuspenseBoundary {
|
||||||
vnode: VNode<RendererNode, RendererElement, SuspenseProps>
|
vnode: VNode<RendererNode, RendererElement, SuspenseProps>
|
||||||
parent: SuspenseBoundary | null
|
parent: SuspenseBoundary | null
|
||||||
parentComponent: ComponentInternalInstance | null
|
parentComponent: ComponentInternalInstance | null
|
||||||
isSVG: boolean
|
namespace: ElementNamespace
|
||||||
container: RendererElement
|
container: RendererElement
|
||||||
hiddenContainer: RendererElement
|
hiddenContainer: RendererElement
|
||||||
anchor: RendererNode | null
|
anchor: RendererNode | null
|
||||||
|
@ -413,7 +414,7 @@ function createSuspenseBoundary(
|
||||||
container: RendererElement,
|
container: RendererElement,
|
||||||
hiddenContainer: RendererElement,
|
hiddenContainer: RendererElement,
|
||||||
anchor: RendererNode | null,
|
anchor: RendererNode | null,
|
||||||
isSVG: boolean,
|
namespace: ElementNamespace,
|
||||||
slotScopeIds: string[] | null,
|
slotScopeIds: string[] | null,
|
||||||
optimized: boolean,
|
optimized: boolean,
|
||||||
rendererInternals: RendererInternals,
|
rendererInternals: RendererInternals,
|
||||||
|
@ -455,7 +456,7 @@ function createSuspenseBoundary(
|
||||||
vnode,
|
vnode,
|
||||||
parent: parentSuspense,
|
parent: parentSuspense,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
isSVG,
|
namespace,
|
||||||
container,
|
container,
|
||||||
hiddenContainer,
|
hiddenContainer,
|
||||||
anchor,
|
anchor,
|
||||||
|
@ -576,7 +577,7 @@ function createSuspenseBoundary(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const { vnode, activeBranch, parentComponent, container, isSVG } =
|
const { vnode, activeBranch, parentComponent, container, namespace } =
|
||||||
suspense
|
suspense
|
||||||
|
|
||||||
// invoke @fallback event
|
// invoke @fallback event
|
||||||
|
@ -594,7 +595,7 @@ function createSuspenseBoundary(
|
||||||
next(activeBranch!),
|
next(activeBranch!),
|
||||||
parentComponent,
|
parentComponent,
|
||||||
null, // fallback tree will not have suspense context
|
null, // fallback tree will not have suspense context
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
optimized
|
optimized
|
||||||
)
|
)
|
||||||
|
@ -675,7 +676,7 @@ function createSuspenseBoundary(
|
||||||
// consider the comment placeholder case.
|
// consider the comment placeholder case.
|
||||||
hydratedEl ? null : next(instance.subTree),
|
hydratedEl ? null : next(instance.subTree),
|
||||||
suspense,
|
suspense,
|
||||||
isSVG,
|
namespace,
|
||||||
optimized
|
optimized
|
||||||
)
|
)
|
||||||
if (placeholder) {
|
if (placeholder) {
|
||||||
|
@ -721,7 +722,7 @@ function hydrateSuspense(
|
||||||
vnode: VNode,
|
vnode: VNode,
|
||||||
parentComponent: ComponentInternalInstance | null,
|
parentComponent: ComponentInternalInstance | null,
|
||||||
parentSuspense: SuspenseBoundary | null,
|
parentSuspense: SuspenseBoundary | null,
|
||||||
isSVG: boolean,
|
namespace: ElementNamespace,
|
||||||
slotScopeIds: string[] | null,
|
slotScopeIds: string[] | null,
|
||||||
optimized: boolean,
|
optimized: boolean,
|
||||||
rendererInternals: RendererInternals,
|
rendererInternals: RendererInternals,
|
||||||
|
@ -742,7 +743,7 @@ function hydrateSuspense(
|
||||||
node.parentNode!,
|
node.parentNode!,
|
||||||
document.createElement('div'),
|
document.createElement('div'),
|
||||||
null,
|
null,
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
optimized,
|
optimized,
|
||||||
rendererInternals,
|
rendererInternals,
|
||||||
|
|
|
@ -6,7 +6,8 @@ import {
|
||||||
RendererElement,
|
RendererElement,
|
||||||
RendererNode,
|
RendererNode,
|
||||||
RendererOptions,
|
RendererOptions,
|
||||||
traverseStaticChildren
|
traverseStaticChildren,
|
||||||
|
ElementNamespace
|
||||||
} from '../renderer'
|
} from '../renderer'
|
||||||
import { VNode, VNodeArrayChildren, VNodeProps } from '../vnode'
|
import { VNode, VNodeArrayChildren, VNodeProps } from '../vnode'
|
||||||
import { isString, ShapeFlags } from '@vue/shared'
|
import { isString, ShapeFlags } from '@vue/shared'
|
||||||
|
@ -28,6 +29,9 @@ const isTeleportDisabled = (props: VNode['props']): boolean =>
|
||||||
const isTargetSVG = (target: RendererElement): boolean =>
|
const isTargetSVG = (target: RendererElement): boolean =>
|
||||||
typeof SVGElement !== 'undefined' && target instanceof SVGElement
|
typeof SVGElement !== 'undefined' && target instanceof SVGElement
|
||||||
|
|
||||||
|
const isTargetMathML = (target: RendererElement): boolean =>
|
||||||
|
typeof MathMLElement === 'function' && target instanceof MathMLElement
|
||||||
|
|
||||||
const resolveTarget = <T = RendererElement>(
|
const resolveTarget = <T = RendererElement>(
|
||||||
props: TeleportProps | null,
|
props: TeleportProps | null,
|
||||||
select: RendererOptions['querySelector']
|
select: RendererOptions['querySelector']
|
||||||
|
@ -72,7 +76,7 @@ export const TeleportImpl = {
|
||||||
anchor: RendererNode | null,
|
anchor: RendererNode | null,
|
||||||
parentComponent: ComponentInternalInstance | null,
|
parentComponent: ComponentInternalInstance | null,
|
||||||
parentSuspense: SuspenseBoundary | null,
|
parentSuspense: SuspenseBoundary | null,
|
||||||
isSVG: boolean,
|
namespace: ElementNamespace,
|
||||||
slotScopeIds: string[] | null,
|
slotScopeIds: string[] | null,
|
||||||
optimized: boolean,
|
optimized: boolean,
|
||||||
internals: RendererInternals
|
internals: RendererInternals
|
||||||
|
@ -109,7 +113,11 @@ export const TeleportImpl = {
|
||||||
if (target) {
|
if (target) {
|
||||||
insert(targetAnchor, target)
|
insert(targetAnchor, target)
|
||||||
// #2652 we could be teleporting from a non-SVG tree into an SVG tree
|
// #2652 we could be teleporting from a non-SVG tree into an SVG tree
|
||||||
isSVG = isSVG || isTargetSVG(target)
|
if (namespace === 'svg' || isTargetSVG(target)) {
|
||||||
|
namespace = 'svg'
|
||||||
|
} else if (namespace === 'mathml' || isTargetMathML(target)) {
|
||||||
|
namespace = 'mathml'
|
||||||
|
}
|
||||||
} else if (__DEV__ && !disabled) {
|
} else if (__DEV__ && !disabled) {
|
||||||
warn('Invalid Teleport target on mount:', target, `(${typeof target})`)
|
warn('Invalid Teleport target on mount:', target, `(${typeof target})`)
|
||||||
}
|
}
|
||||||
|
@ -124,7 +132,7 @@ export const TeleportImpl = {
|
||||||
anchor,
|
anchor,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
optimized
|
optimized
|
||||||
)
|
)
|
||||||
|
@ -145,7 +153,12 @@ export const TeleportImpl = {
|
||||||
const wasDisabled = isTeleportDisabled(n1.props)
|
const wasDisabled = isTeleportDisabled(n1.props)
|
||||||
const currentContainer = wasDisabled ? container : target
|
const currentContainer = wasDisabled ? container : target
|
||||||
const currentAnchor = wasDisabled ? mainAnchor : targetAnchor
|
const currentAnchor = wasDisabled ? mainAnchor : targetAnchor
|
||||||
isSVG = isSVG || isTargetSVG(target)
|
|
||||||
|
if (namespace === 'svg' || isTargetSVG(target)) {
|
||||||
|
namespace = 'svg'
|
||||||
|
} else if (namespace === 'mathml' || isTargetMathML(target)) {
|
||||||
|
namespace = 'mathml'
|
||||||
|
}
|
||||||
|
|
||||||
if (dynamicChildren) {
|
if (dynamicChildren) {
|
||||||
// fast path when the teleport happens to be a block root
|
// fast path when the teleport happens to be a block root
|
||||||
|
@ -155,7 +168,7 @@ export const TeleportImpl = {
|
||||||
currentContainer,
|
currentContainer,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds
|
slotScopeIds
|
||||||
)
|
)
|
||||||
// even in block tree mode we need to make sure all root-level nodes
|
// even in block tree mode we need to make sure all root-level nodes
|
||||||
|
@ -170,7 +183,7 @@ export const TeleportImpl = {
|
||||||
currentAnchor,
|
currentAnchor,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
|
|
|
@ -52,7 +52,17 @@ enum DOMNodeTypes {
|
||||||
let hasMismatch = false
|
let hasMismatch = false
|
||||||
|
|
||||||
const isSVGContainer = (container: Element) =>
|
const isSVGContainer = (container: Element) =>
|
||||||
/svg/.test(container.namespaceURI!) && container.tagName !== 'foreignObject'
|
container.namespaceURI!.includes('svg') &&
|
||||||
|
container.tagName !== 'foreignObject'
|
||||||
|
|
||||||
|
const isMathMLContainer = (container: Element) =>
|
||||||
|
container.namespaceURI!.includes('MathML')
|
||||||
|
|
||||||
|
const getContainerType = (container: Element): 'svg' | 'mathml' | undefined => {
|
||||||
|
if (isSVGContainer(container)) return 'svg'
|
||||||
|
if (isMathMLContainer(container)) return 'mathml'
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
const isComment = (node: Node): node is Comment =>
|
const isComment = (node: Node): node is Comment =>
|
||||||
node.nodeType === DOMNodeTypes.COMMENT
|
node.nodeType === DOMNodeTypes.COMMENT
|
||||||
|
@ -277,7 +287,7 @@ export function createHydrationFunctions(
|
||||||
null,
|
null,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVGContainer(container),
|
getContainerType(container),
|
||||||
optimized
|
optimized
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -320,7 +330,7 @@ export function createHydrationFunctions(
|
||||||
vnode,
|
vnode,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVGContainer(parentNode(node)!),
|
getContainerType(parentNode(node)!),
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
optimized,
|
optimized,
|
||||||
rendererInternals,
|
rendererInternals,
|
||||||
|
@ -453,7 +463,7 @@ export function createHydrationFunctions(
|
||||||
key,
|
key,
|
||||||
null,
|
null,
|
||||||
props[key],
|
props[key],
|
||||||
false,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
parentComponent
|
parentComponent
|
||||||
)
|
)
|
||||||
|
@ -467,7 +477,7 @@ export function createHydrationFunctions(
|
||||||
'onClick',
|
'onClick',
|
||||||
null,
|
null,
|
||||||
props.onClick,
|
props.onClick,
|
||||||
false,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
parentComponent
|
parentComponent
|
||||||
)
|
)
|
||||||
|
@ -547,7 +557,7 @@ export function createHydrationFunctions(
|
||||||
null,
|
null,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVGContainer(container),
|
getContainerType(container),
|
||||||
slotScopeIds
|
slotScopeIds
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -639,7 +649,7 @@ export function createHydrationFunctions(
|
||||||
next,
|
next,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVGContainer(container),
|
getContainerType(container),
|
||||||
slotScopeIds
|
slotScopeIds
|
||||||
)
|
)
|
||||||
return next
|
return next
|
||||||
|
|
|
@ -260,7 +260,8 @@ export type {
|
||||||
RendererElement,
|
RendererElement,
|
||||||
HydrationRenderer,
|
HydrationRenderer,
|
||||||
RendererOptions,
|
RendererOptions,
|
||||||
RootRenderFunction
|
RootRenderFunction,
|
||||||
|
ElementNamespace
|
||||||
} from './renderer'
|
} from './renderer'
|
||||||
export type { RootHydrateFunction } from './hydration'
|
export type { RootHydrateFunction } from './hydration'
|
||||||
export type { Slot, Slots, SlotsType } from './componentSlots'
|
export type { Slot, Slots, SlotsType } from './componentSlots'
|
||||||
|
|
|
@ -83,10 +83,12 @@ export interface HydrationRenderer extends Renderer<Element | ShadowRoot> {
|
||||||
hydrate: RootHydrateFunction
|
hydrate: RootHydrateFunction
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ElementNamespace = 'svg' | 'mathml' | undefined
|
||||||
|
|
||||||
export type RootRenderFunction<HostElement = RendererElement> = (
|
export type RootRenderFunction<HostElement = RendererElement> = (
|
||||||
vnode: VNode | null,
|
vnode: VNode | null,
|
||||||
container: HostElement,
|
container: HostElement,
|
||||||
isSVG?: boolean
|
namespace?: ElementNamespace
|
||||||
) => void
|
) => void
|
||||||
|
|
||||||
export interface RendererOptions<
|
export interface RendererOptions<
|
||||||
|
@ -98,7 +100,7 @@ export interface RendererOptions<
|
||||||
key: string,
|
key: string,
|
||||||
prevValue: any,
|
prevValue: any,
|
||||||
nextValue: any,
|
nextValue: any,
|
||||||
isSVG?: boolean,
|
namespace?: ElementNamespace,
|
||||||
prevChildren?: VNode<HostNode, HostElement>[],
|
prevChildren?: VNode<HostNode, HostElement>[],
|
||||||
parentComponent?: ComponentInternalInstance | null,
|
parentComponent?: ComponentInternalInstance | null,
|
||||||
parentSuspense?: SuspenseBoundary | null,
|
parentSuspense?: SuspenseBoundary | null,
|
||||||
|
@ -108,7 +110,7 @@ export interface RendererOptions<
|
||||||
remove(el: HostNode): void
|
remove(el: HostNode): void
|
||||||
createElement(
|
createElement(
|
||||||
type: string,
|
type: string,
|
||||||
isSVG?: boolean,
|
namespace?: ElementNamespace,
|
||||||
isCustomizedBuiltIn?: string,
|
isCustomizedBuiltIn?: string,
|
||||||
vnodeProps?: (VNodeProps & { [key: string]: any }) | null
|
vnodeProps?: (VNodeProps & { [key: string]: any }) | null
|
||||||
): HostElement
|
): HostElement
|
||||||
|
@ -125,7 +127,7 @@ export interface RendererOptions<
|
||||||
content: string,
|
content: string,
|
||||||
parent: HostElement,
|
parent: HostElement,
|
||||||
anchor: HostNode | null,
|
anchor: HostNode | null,
|
||||||
isSVG: boolean,
|
namespace: ElementNamespace,
|
||||||
start?: HostNode | null,
|
start?: HostNode | null,
|
||||||
end?: HostNode | null
|
end?: HostNode | null
|
||||||
): [HostNode, HostNode]
|
): [HostNode, HostNode]
|
||||||
|
@ -170,7 +172,7 @@ type PatchFn = (
|
||||||
anchor?: RendererNode | null,
|
anchor?: RendererNode | null,
|
||||||
parentComponent?: ComponentInternalInstance | null,
|
parentComponent?: ComponentInternalInstance | null,
|
||||||
parentSuspense?: SuspenseBoundary | null,
|
parentSuspense?: SuspenseBoundary | null,
|
||||||
isSVG?: boolean,
|
namespace?: ElementNamespace,
|
||||||
slotScopeIds?: string[] | null,
|
slotScopeIds?: string[] | null,
|
||||||
optimized?: boolean
|
optimized?: boolean
|
||||||
) => void
|
) => void
|
||||||
|
@ -181,7 +183,7 @@ type MountChildrenFn = (
|
||||||
anchor: RendererNode | null,
|
anchor: RendererNode | null,
|
||||||
parentComponent: ComponentInternalInstance | null,
|
parentComponent: ComponentInternalInstance | null,
|
||||||
parentSuspense: SuspenseBoundary | null,
|
parentSuspense: SuspenseBoundary | null,
|
||||||
isSVG: boolean,
|
namespace: ElementNamespace,
|
||||||
slotScopeIds: string[] | null,
|
slotScopeIds: string[] | null,
|
||||||
optimized: boolean,
|
optimized: boolean,
|
||||||
start?: number
|
start?: number
|
||||||
|
@ -194,7 +196,7 @@ type PatchChildrenFn = (
|
||||||
anchor: RendererNode | null,
|
anchor: RendererNode | null,
|
||||||
parentComponent: ComponentInternalInstance | null,
|
parentComponent: ComponentInternalInstance | null,
|
||||||
parentSuspense: SuspenseBoundary | null,
|
parentSuspense: SuspenseBoundary | null,
|
||||||
isSVG: boolean,
|
namespace: ElementNamespace,
|
||||||
slotScopeIds: string[] | null,
|
slotScopeIds: string[] | null,
|
||||||
optimized: boolean
|
optimized: boolean
|
||||||
) => void
|
) => void
|
||||||
|
@ -205,7 +207,7 @@ type PatchBlockChildrenFn = (
|
||||||
fallbackContainer: RendererElement,
|
fallbackContainer: RendererElement,
|
||||||
parentComponent: ComponentInternalInstance | null,
|
parentComponent: ComponentInternalInstance | null,
|
||||||
parentSuspense: SuspenseBoundary | null,
|
parentSuspense: SuspenseBoundary | null,
|
||||||
isSVG: boolean,
|
namespace: ElementNamespace,
|
||||||
slotScopeIds: string[] | null
|
slotScopeIds: string[] | null
|
||||||
) => void
|
) => void
|
||||||
|
|
||||||
|
@ -244,7 +246,7 @@ export type MountComponentFn = (
|
||||||
anchor: RendererNode | null,
|
anchor: RendererNode | null,
|
||||||
parentComponent: ComponentInternalInstance | null,
|
parentComponent: ComponentInternalInstance | null,
|
||||||
parentSuspense: SuspenseBoundary | null,
|
parentSuspense: SuspenseBoundary | null,
|
||||||
isSVG: boolean,
|
namespace: ElementNamespace,
|
||||||
optimized: boolean
|
optimized: boolean
|
||||||
) => void
|
) => void
|
||||||
|
|
||||||
|
@ -261,7 +263,7 @@ export type SetupRenderEffectFn = (
|
||||||
container: RendererElement,
|
container: RendererElement,
|
||||||
anchor: RendererNode | null,
|
anchor: RendererNode | null,
|
||||||
parentSuspense: SuspenseBoundary | null,
|
parentSuspense: SuspenseBoundary | null,
|
||||||
isSVG: boolean,
|
namespace: ElementNamespace,
|
||||||
optimized: boolean
|
optimized: boolean
|
||||||
) => void
|
) => void
|
||||||
|
|
||||||
|
@ -362,7 +364,7 @@ function baseCreateRenderer(
|
||||||
anchor = null,
|
anchor = null,
|
||||||
parentComponent = null,
|
parentComponent = null,
|
||||||
parentSuspense = null,
|
parentSuspense = null,
|
||||||
isSVG = false,
|
namespace = undefined,
|
||||||
slotScopeIds = null,
|
slotScopeIds = null,
|
||||||
optimized = __DEV__ && isHmrUpdating ? false : !!n2.dynamicChildren
|
optimized = __DEV__ && isHmrUpdating ? false : !!n2.dynamicChildren
|
||||||
) => {
|
) => {
|
||||||
|
@ -392,9 +394,9 @@ function baseCreateRenderer(
|
||||||
break
|
break
|
||||||
case Static:
|
case Static:
|
||||||
if (n1 == null) {
|
if (n1 == null) {
|
||||||
mountStaticNode(n2, container, anchor, isSVG)
|
mountStaticNode(n2, container, anchor, namespace)
|
||||||
} else if (__DEV__) {
|
} else if (__DEV__) {
|
||||||
patchStaticNode(n1, n2, container, isSVG)
|
patchStaticNode(n1, n2, container, namespace)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case Fragment:
|
case Fragment:
|
||||||
|
@ -405,7 +407,7 @@ function baseCreateRenderer(
|
||||||
anchor,
|
anchor,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
optimized
|
optimized
|
||||||
)
|
)
|
||||||
|
@ -419,7 +421,7 @@ function baseCreateRenderer(
|
||||||
anchor,
|
anchor,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
optimized
|
optimized
|
||||||
)
|
)
|
||||||
|
@ -431,7 +433,7 @@ function baseCreateRenderer(
|
||||||
anchor,
|
anchor,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
optimized
|
optimized
|
||||||
)
|
)
|
||||||
|
@ -443,7 +445,7 @@ function baseCreateRenderer(
|
||||||
anchor,
|
anchor,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
optimized,
|
optimized,
|
||||||
internals
|
internals
|
||||||
|
@ -456,7 +458,7 @@ function baseCreateRenderer(
|
||||||
anchor,
|
anchor,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
optimized,
|
optimized,
|
||||||
internals
|
internals
|
||||||
|
@ -509,7 +511,7 @@ function baseCreateRenderer(
|
||||||
n2: VNode,
|
n2: VNode,
|
||||||
container: RendererElement,
|
container: RendererElement,
|
||||||
anchor: RendererNode | null,
|
anchor: RendererNode | null,
|
||||||
isSVG: boolean
|
namespace: ElementNamespace
|
||||||
) => {
|
) => {
|
||||||
// static nodes are only present when used with compiler-dom/runtime-dom
|
// static nodes are only present when used with compiler-dom/runtime-dom
|
||||||
// which guarantees presence of hostInsertStaticContent.
|
// which guarantees presence of hostInsertStaticContent.
|
||||||
|
@ -517,7 +519,7 @@ function baseCreateRenderer(
|
||||||
n2.children as string,
|
n2.children as string,
|
||||||
container,
|
container,
|
||||||
anchor,
|
anchor,
|
||||||
isSVG,
|
namespace,
|
||||||
n2.el,
|
n2.el,
|
||||||
n2.anchor
|
n2.anchor
|
||||||
)
|
)
|
||||||
|
@ -530,7 +532,7 @@ function baseCreateRenderer(
|
||||||
n1: VNode,
|
n1: VNode,
|
||||||
n2: VNode,
|
n2: VNode,
|
||||||
container: RendererElement,
|
container: RendererElement,
|
||||||
isSVG: boolean
|
namespace: ElementNamespace
|
||||||
) => {
|
) => {
|
||||||
// static nodes are only patched during dev for HMR
|
// static nodes are only patched during dev for HMR
|
||||||
if (n2.children !== n1.children) {
|
if (n2.children !== n1.children) {
|
||||||
|
@ -542,7 +544,7 @@ function baseCreateRenderer(
|
||||||
n2.children as string,
|
n2.children as string,
|
||||||
container,
|
container,
|
||||||
anchor,
|
anchor,
|
||||||
isSVG
|
namespace
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
n2.el = n1.el
|
n2.el = n1.el
|
||||||
|
@ -581,11 +583,16 @@ function baseCreateRenderer(
|
||||||
anchor: RendererNode | null,
|
anchor: RendererNode | null,
|
||||||
parentComponent: ComponentInternalInstance | null,
|
parentComponent: ComponentInternalInstance | null,
|
||||||
parentSuspense: SuspenseBoundary | null,
|
parentSuspense: SuspenseBoundary | null,
|
||||||
isSVG: boolean,
|
namespace: ElementNamespace,
|
||||||
slotScopeIds: string[] | null,
|
slotScopeIds: string[] | null,
|
||||||
optimized: boolean
|
optimized: boolean
|
||||||
) => {
|
) => {
|
||||||
isSVG = isSVG || n2.type === 'svg'
|
if (n2.type === 'svg') {
|
||||||
|
namespace = 'svg'
|
||||||
|
} else if (n2.type === 'math') {
|
||||||
|
namespace = 'mathml'
|
||||||
|
}
|
||||||
|
|
||||||
if (n1 == null) {
|
if (n1 == null) {
|
||||||
mountElement(
|
mountElement(
|
||||||
n2,
|
n2,
|
||||||
|
@ -593,7 +600,7 @@ function baseCreateRenderer(
|
||||||
anchor,
|
anchor,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
optimized
|
optimized
|
||||||
)
|
)
|
||||||
|
@ -603,7 +610,7 @@ function baseCreateRenderer(
|
||||||
n2,
|
n2,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
optimized
|
optimized
|
||||||
)
|
)
|
||||||
|
@ -616,17 +623,17 @@ function baseCreateRenderer(
|
||||||
anchor: RendererNode | null,
|
anchor: RendererNode | null,
|
||||||
parentComponent: ComponentInternalInstance | null,
|
parentComponent: ComponentInternalInstance | null,
|
||||||
parentSuspense: SuspenseBoundary | null,
|
parentSuspense: SuspenseBoundary | null,
|
||||||
isSVG: boolean,
|
namespace: ElementNamespace,
|
||||||
slotScopeIds: string[] | null,
|
slotScopeIds: string[] | null,
|
||||||
optimized: boolean
|
optimized: boolean
|
||||||
) => {
|
) => {
|
||||||
let el: RendererElement
|
let el: RendererElement
|
||||||
let vnodeHook: VNodeHook | undefined | null
|
let vnodeHook: VNodeHook | undefined | null
|
||||||
const { type, props, shapeFlag, transition, dirs } = vnode
|
const { props, shapeFlag, transition, dirs } = vnode
|
||||||
|
|
||||||
el = vnode.el = hostCreateElement(
|
el = vnode.el = hostCreateElement(
|
||||||
vnode.type as string,
|
vnode.type as string,
|
||||||
isSVG,
|
namespace,
|
||||||
props && props.is,
|
props && props.is,
|
||||||
props
|
props
|
||||||
)
|
)
|
||||||
|
@ -642,7 +649,7 @@ function baseCreateRenderer(
|
||||||
null,
|
null,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG && type !== 'foreignObject',
|
resolveChildrenNamespace(vnode, namespace),
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
optimized
|
optimized
|
||||||
)
|
)
|
||||||
|
@ -662,7 +669,7 @@ function baseCreateRenderer(
|
||||||
key,
|
key,
|
||||||
null,
|
null,
|
||||||
props[key],
|
props[key],
|
||||||
isSVG,
|
namespace,
|
||||||
vnode.children as VNode[],
|
vnode.children as VNode[],
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
|
@ -680,7 +687,7 @@ function baseCreateRenderer(
|
||||||
* affect non-DOM renderers)
|
* affect non-DOM renderers)
|
||||||
*/
|
*/
|
||||||
if ('value' in props) {
|
if ('value' in props) {
|
||||||
hostPatchProp(el, 'value', null, props.value)
|
hostPatchProp(el, 'value', null, props.value, namespace)
|
||||||
}
|
}
|
||||||
if ((vnodeHook = props.onVnodeBeforeMount)) {
|
if ((vnodeHook = props.onVnodeBeforeMount)) {
|
||||||
invokeVNodeHook(vnodeHook, parentComponent, vnode)
|
invokeVNodeHook(vnodeHook, parentComponent, vnode)
|
||||||
|
@ -764,7 +771,7 @@ function baseCreateRenderer(
|
||||||
anchor,
|
anchor,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG,
|
namespace: ElementNamespace,
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
optimized,
|
optimized,
|
||||||
start = 0
|
start = 0
|
||||||
|
@ -780,7 +787,7 @@ function baseCreateRenderer(
|
||||||
anchor,
|
anchor,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
optimized
|
optimized
|
||||||
)
|
)
|
||||||
|
@ -792,7 +799,7 @@ function baseCreateRenderer(
|
||||||
n2: VNode,
|
n2: VNode,
|
||||||
parentComponent: ComponentInternalInstance | null,
|
parentComponent: ComponentInternalInstance | null,
|
||||||
parentSuspense: SuspenseBoundary | null,
|
parentSuspense: SuspenseBoundary | null,
|
||||||
isSVG: boolean,
|
namespace: ElementNamespace,
|
||||||
slotScopeIds: string[] | null,
|
slotScopeIds: string[] | null,
|
||||||
optimized: boolean
|
optimized: boolean
|
||||||
) => {
|
) => {
|
||||||
|
@ -822,7 +829,6 @@ function baseCreateRenderer(
|
||||||
dynamicChildren = null
|
dynamicChildren = null
|
||||||
}
|
}
|
||||||
|
|
||||||
const areChildrenSVG = isSVG && n2.type !== 'foreignObject'
|
|
||||||
if (dynamicChildren) {
|
if (dynamicChildren) {
|
||||||
patchBlockChildren(
|
patchBlockChildren(
|
||||||
n1.dynamicChildren!,
|
n1.dynamicChildren!,
|
||||||
|
@ -830,7 +836,7 @@ function baseCreateRenderer(
|
||||||
el,
|
el,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
areChildrenSVG,
|
resolveChildrenNamespace(n2, namespace),
|
||||||
slotScopeIds
|
slotScopeIds
|
||||||
)
|
)
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
|
@ -846,7 +852,7 @@ function baseCreateRenderer(
|
||||||
null,
|
null,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
areChildrenSVG,
|
resolveChildrenNamespace(n2, namespace),
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
|
@ -866,21 +872,21 @@ function baseCreateRenderer(
|
||||||
newProps,
|
newProps,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG
|
namespace
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
// class
|
// class
|
||||||
// this flag is matched when the element has dynamic class bindings.
|
// this flag is matched when the element has dynamic class bindings.
|
||||||
if (patchFlag & PatchFlags.CLASS) {
|
if (patchFlag & PatchFlags.CLASS) {
|
||||||
if (oldProps.class !== newProps.class) {
|
if (oldProps.class !== newProps.class) {
|
||||||
hostPatchProp(el, 'class', null, newProps.class, isSVG)
|
hostPatchProp(el, 'class', null, newProps.class, namespace)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// style
|
// style
|
||||||
// this flag is matched when the element has dynamic style bindings
|
// this flag is matched when the element has dynamic style bindings
|
||||||
if (patchFlag & PatchFlags.STYLE) {
|
if (patchFlag & PatchFlags.STYLE) {
|
||||||
hostPatchProp(el, 'style', oldProps.style, newProps.style, isSVG)
|
hostPatchProp(el, 'style', oldProps.style, newProps.style, namespace)
|
||||||
}
|
}
|
||||||
|
|
||||||
// props
|
// props
|
||||||
|
@ -903,7 +909,7 @@ function baseCreateRenderer(
|
||||||
key,
|
key,
|
||||||
prev,
|
prev,
|
||||||
next,
|
next,
|
||||||
isSVG,
|
namespace,
|
||||||
n1.children as VNode[],
|
n1.children as VNode[],
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
|
@ -930,7 +936,7 @@ function baseCreateRenderer(
|
||||||
newProps,
|
newProps,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG
|
namespace
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -949,7 +955,7 @@ function baseCreateRenderer(
|
||||||
fallbackContainer,
|
fallbackContainer,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG,
|
namespace: ElementNamespace,
|
||||||
slotScopeIds
|
slotScopeIds
|
||||||
) => {
|
) => {
|
||||||
for (let i = 0; i < newChildren.length; i++) {
|
for (let i = 0; i < newChildren.length; i++) {
|
||||||
|
@ -979,7 +985,7 @@ function baseCreateRenderer(
|
||||||
null,
|
null,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
|
@ -993,7 +999,7 @@ function baseCreateRenderer(
|
||||||
newProps: Data,
|
newProps: Data,
|
||||||
parentComponent: ComponentInternalInstance | null,
|
parentComponent: ComponentInternalInstance | null,
|
||||||
parentSuspense: SuspenseBoundary | null,
|
parentSuspense: SuspenseBoundary | null,
|
||||||
isSVG: boolean
|
namespace: ElementNamespace
|
||||||
) => {
|
) => {
|
||||||
if (oldProps !== newProps) {
|
if (oldProps !== newProps) {
|
||||||
if (oldProps !== EMPTY_OBJ) {
|
if (oldProps !== EMPTY_OBJ) {
|
||||||
|
@ -1004,7 +1010,7 @@ function baseCreateRenderer(
|
||||||
key,
|
key,
|
||||||
oldProps[key],
|
oldProps[key],
|
||||||
null,
|
null,
|
||||||
isSVG,
|
namespace,
|
||||||
vnode.children as VNode[],
|
vnode.children as VNode[],
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
|
@ -1025,7 +1031,7 @@ function baseCreateRenderer(
|
||||||
key,
|
key,
|
||||||
prev,
|
prev,
|
||||||
next,
|
next,
|
||||||
isSVG,
|
namespace,
|
||||||
vnode.children as VNode[],
|
vnode.children as VNode[],
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
|
@ -1034,7 +1040,7 @@ function baseCreateRenderer(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ('value' in newProps) {
|
if ('value' in newProps) {
|
||||||
hostPatchProp(el, 'value', oldProps.value, newProps.value)
|
hostPatchProp(el, 'value', oldProps.value, newProps.value, namespace)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1046,7 +1052,7 @@ function baseCreateRenderer(
|
||||||
anchor: RendererNode | null,
|
anchor: RendererNode | null,
|
||||||
parentComponent: ComponentInternalInstance | null,
|
parentComponent: ComponentInternalInstance | null,
|
||||||
parentSuspense: SuspenseBoundary | null,
|
parentSuspense: SuspenseBoundary | null,
|
||||||
isSVG: boolean,
|
namespace: ElementNamespace,
|
||||||
slotScopeIds: string[] | null,
|
slotScopeIds: string[] | null,
|
||||||
optimized: boolean
|
optimized: boolean
|
||||||
) => {
|
) => {
|
||||||
|
@ -1085,7 +1091,7 @@ function baseCreateRenderer(
|
||||||
fragmentEndAnchor,
|
fragmentEndAnchor,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
optimized
|
optimized
|
||||||
)
|
)
|
||||||
|
@ -1106,7 +1112,7 @@ function baseCreateRenderer(
|
||||||
container,
|
container,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds
|
slotScopeIds
|
||||||
)
|
)
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
|
@ -1134,7 +1140,7 @@ function baseCreateRenderer(
|
||||||
fragmentEndAnchor,
|
fragmentEndAnchor,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
optimized
|
optimized
|
||||||
)
|
)
|
||||||
|
@ -1149,7 +1155,7 @@ function baseCreateRenderer(
|
||||||
anchor: RendererNode | null,
|
anchor: RendererNode | null,
|
||||||
parentComponent: ComponentInternalInstance | null,
|
parentComponent: ComponentInternalInstance | null,
|
||||||
parentSuspense: SuspenseBoundary | null,
|
parentSuspense: SuspenseBoundary | null,
|
||||||
isSVG: boolean,
|
namespace: ElementNamespace,
|
||||||
slotScopeIds: string[] | null,
|
slotScopeIds: string[] | null,
|
||||||
optimized: boolean
|
optimized: boolean
|
||||||
) => {
|
) => {
|
||||||
|
@ -1160,7 +1166,7 @@ function baseCreateRenderer(
|
||||||
n2,
|
n2,
|
||||||
container,
|
container,
|
||||||
anchor,
|
anchor,
|
||||||
isSVG,
|
namespace,
|
||||||
optimized
|
optimized
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@ -1170,7 +1176,7 @@ function baseCreateRenderer(
|
||||||
anchor,
|
anchor,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG,
|
namespace,
|
||||||
optimized
|
optimized
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1185,7 +1191,7 @@ function baseCreateRenderer(
|
||||||
anchor,
|
anchor,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG,
|
namespace: ElementNamespace,
|
||||||
optimized
|
optimized
|
||||||
) => {
|
) => {
|
||||||
// 2.x compat may pre-create the component instance before actually
|
// 2.x compat may pre-create the component instance before actually
|
||||||
|
@ -1245,7 +1251,7 @@ function baseCreateRenderer(
|
||||||
container,
|
container,
|
||||||
anchor,
|
anchor,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG,
|
namespace,
|
||||||
optimized
|
optimized
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1296,7 +1302,7 @@ function baseCreateRenderer(
|
||||||
container,
|
container,
|
||||||
anchor,
|
anchor,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG,
|
namespace: ElementNamespace,
|
||||||
optimized
|
optimized
|
||||||
) => {
|
) => {
|
||||||
const componentUpdateFn = () => {
|
const componentUpdateFn = () => {
|
||||||
|
@ -1380,7 +1386,7 @@ function baseCreateRenderer(
|
||||||
anchor,
|
anchor,
|
||||||
instance,
|
instance,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG
|
namespace
|
||||||
)
|
)
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
endMeasure(instance, `patch`)
|
endMeasure(instance, `patch`)
|
||||||
|
@ -1499,7 +1505,7 @@ function baseCreateRenderer(
|
||||||
getNextHostNode(prevTree),
|
getNextHostNode(prevTree),
|
||||||
instance,
|
instance,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG
|
namespace
|
||||||
)
|
)
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
endMeasure(instance, `patch`)
|
endMeasure(instance, `patch`)
|
||||||
|
@ -1599,7 +1605,7 @@ function baseCreateRenderer(
|
||||||
anchor,
|
anchor,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG,
|
namespace: ElementNamespace,
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
optimized = false
|
optimized = false
|
||||||
) => {
|
) => {
|
||||||
|
@ -1620,7 +1626,7 @@ function baseCreateRenderer(
|
||||||
anchor,
|
anchor,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
optimized
|
optimized
|
||||||
)
|
)
|
||||||
|
@ -1634,7 +1640,7 @@ function baseCreateRenderer(
|
||||||
anchor,
|
anchor,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
optimized
|
optimized
|
||||||
)
|
)
|
||||||
|
@ -1663,7 +1669,7 @@ function baseCreateRenderer(
|
||||||
anchor,
|
anchor,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
optimized
|
optimized
|
||||||
)
|
)
|
||||||
|
@ -1685,7 +1691,7 @@ function baseCreateRenderer(
|
||||||
anchor,
|
anchor,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
optimized
|
optimized
|
||||||
)
|
)
|
||||||
|
@ -1701,7 +1707,7 @@ function baseCreateRenderer(
|
||||||
anchor: RendererNode | null,
|
anchor: RendererNode | null,
|
||||||
parentComponent: ComponentInternalInstance | null,
|
parentComponent: ComponentInternalInstance | null,
|
||||||
parentSuspense: SuspenseBoundary | null,
|
parentSuspense: SuspenseBoundary | null,
|
||||||
isSVG: boolean,
|
namespace: ElementNamespace,
|
||||||
slotScopeIds: string[] | null,
|
slotScopeIds: string[] | null,
|
||||||
optimized: boolean
|
optimized: boolean
|
||||||
) => {
|
) => {
|
||||||
|
@ -1722,7 +1728,7 @@ function baseCreateRenderer(
|
||||||
null,
|
null,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
optimized
|
optimized
|
||||||
)
|
)
|
||||||
|
@ -1745,7 +1751,7 @@ function baseCreateRenderer(
|
||||||
anchor,
|
anchor,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
optimized,
|
optimized,
|
||||||
commonLength
|
commonLength
|
||||||
|
@ -1761,7 +1767,7 @@ function baseCreateRenderer(
|
||||||
parentAnchor: RendererNode | null,
|
parentAnchor: RendererNode | null,
|
||||||
parentComponent: ComponentInternalInstance | null,
|
parentComponent: ComponentInternalInstance | null,
|
||||||
parentSuspense: SuspenseBoundary | null,
|
parentSuspense: SuspenseBoundary | null,
|
||||||
isSVG: boolean,
|
namespace: ElementNamespace,
|
||||||
slotScopeIds: string[] | null,
|
slotScopeIds: string[] | null,
|
||||||
optimized: boolean
|
optimized: boolean
|
||||||
) => {
|
) => {
|
||||||
|
@ -1786,7 +1792,7 @@ function baseCreateRenderer(
|
||||||
null,
|
null,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
optimized
|
optimized
|
||||||
)
|
)
|
||||||
|
@ -1812,7 +1818,7 @@ function baseCreateRenderer(
|
||||||
null,
|
null,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
optimized
|
optimized
|
||||||
)
|
)
|
||||||
|
@ -1844,7 +1850,7 @@ function baseCreateRenderer(
|
||||||
anchor,
|
anchor,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
optimized
|
optimized
|
||||||
)
|
)
|
||||||
|
@ -1947,7 +1953,7 @@ function baseCreateRenderer(
|
||||||
null,
|
null,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
optimized
|
optimized
|
||||||
)
|
)
|
||||||
|
@ -1976,7 +1982,7 @@ function baseCreateRenderer(
|
||||||
anchor,
|
anchor,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
isSVG,
|
namespace,
|
||||||
slotScopeIds,
|
slotScopeIds,
|
||||||
optimized
|
optimized
|
||||||
)
|
)
|
||||||
|
@ -2321,13 +2327,21 @@ function baseCreateRenderer(
|
||||||
return hostNextSibling((vnode.anchor || vnode.el)!)
|
return hostNextSibling((vnode.anchor || vnode.el)!)
|
||||||
}
|
}
|
||||||
|
|
||||||
const render: RootRenderFunction = (vnode, container, isSVG) => {
|
const render: RootRenderFunction = (vnode, container, namespace) => {
|
||||||
if (vnode == null) {
|
if (vnode == null) {
|
||||||
if (container._vnode) {
|
if (container._vnode) {
|
||||||
unmount(container._vnode, null, null, true)
|
unmount(container._vnode, null, null, true)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
patch(container._vnode || null, vnode, container, null, null, null, isSVG)
|
patch(
|
||||||
|
container._vnode || null,
|
||||||
|
vnode,
|
||||||
|
container,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
namespace
|
||||||
|
)
|
||||||
}
|
}
|
||||||
flushPreFlushCbs()
|
flushPreFlushCbs()
|
||||||
flushPostFlushCbs()
|
flushPostFlushCbs()
|
||||||
|
@ -2362,6 +2376,20 @@ function baseCreateRenderer(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resolveChildrenNamespace(
|
||||||
|
{ type, props }: VNode,
|
||||||
|
currentNamespace: ElementNamespace
|
||||||
|
): ElementNamespace {
|
||||||
|
return (currentNamespace === 'svg' && type === 'foreignObject') ||
|
||||||
|
(currentNamespace === 'mathml' &&
|
||||||
|
type === 'annotation-xml' &&
|
||||||
|
props &&
|
||||||
|
props.encoding &&
|
||||||
|
props.encoding.includes('html'))
|
||||||
|
? undefined
|
||||||
|
: currentNamespace
|
||||||
|
}
|
||||||
|
|
||||||
function toggleRecurse(
|
function toggleRecurse(
|
||||||
{ effect, update }: ComponentInternalInstance,
|
{ effect, update }: ComponentInternalInstance,
|
||||||
allowed: boolean
|
allowed: boolean
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { nodeOps, svgNS } from '../src/nodeOps'
|
||||||
|
|
||||||
describe('runtime-dom: node-ops', () => {
|
describe('runtime-dom: node-ops', () => {
|
||||||
test("the <select>'s multiple attr should be set in createElement", () => {
|
test("the <select>'s multiple attr should be set in createElement", () => {
|
||||||
const el = nodeOps.createElement('select', false, undefined, {
|
const el = nodeOps.createElement('select', undefined, undefined, {
|
||||||
multiple: ''
|
multiple: ''
|
||||||
}) as HTMLSelectElement
|
}) as HTMLSelectElement
|
||||||
const option1 = nodeOps.createElement('option') as HTMLOptionElement
|
const option1 = nodeOps.createElement('option') as HTMLOptionElement
|
||||||
|
@ -21,7 +21,12 @@ describe('runtime-dom: node-ops', () => {
|
||||||
test('fresh insertion', () => {
|
test('fresh insertion', () => {
|
||||||
const content = `<div>one</div><div>two</div>three`
|
const content = `<div>one</div><div>two</div>three`
|
||||||
const parent = document.createElement('div')
|
const parent = document.createElement('div')
|
||||||
const nodes = nodeOps.insertStaticContent!(content, parent, null, false)
|
const nodes = nodeOps.insertStaticContent!(
|
||||||
|
content,
|
||||||
|
parent,
|
||||||
|
null,
|
||||||
|
undefined
|
||||||
|
)
|
||||||
expect(parent.innerHTML).toBe(content)
|
expect(parent.innerHTML).toBe(content)
|
||||||
expect(nodes[0]).toBe(parent.firstChild)
|
expect(nodes[0]).toBe(parent.firstChild)
|
||||||
expect(nodes[1]).toBe(parent.lastChild)
|
expect(nodes[1]).toBe(parent.lastChild)
|
||||||
|
@ -33,7 +38,12 @@ describe('runtime-dom: node-ops', () => {
|
||||||
const parent = document.createElement('div')
|
const parent = document.createElement('div')
|
||||||
parent.innerHTML = existing
|
parent.innerHTML = existing
|
||||||
const anchor = parent.firstChild
|
const anchor = parent.firstChild
|
||||||
const nodes = nodeOps.insertStaticContent!(content, parent, anchor, false)
|
const nodes = nodeOps.insertStaticContent!(
|
||||||
|
content,
|
||||||
|
parent,
|
||||||
|
anchor,
|
||||||
|
undefined
|
||||||
|
)
|
||||||
expect(parent.innerHTML).toBe(content + existing)
|
expect(parent.innerHTML).toBe(content + existing)
|
||||||
expect(nodes[0]).toBe(parent.firstChild)
|
expect(nodes[0]).toBe(parent.firstChild)
|
||||||
expect(nodes[1]).toBe(parent.childNodes[parent.childNodes.length - 2])
|
expect(nodes[1]).toBe(parent.childNodes[parent.childNodes.length - 2])
|
||||||
|
@ -46,7 +56,7 @@ describe('runtime-dom: node-ops', () => {
|
||||||
content,
|
content,
|
||||||
parent,
|
parent,
|
||||||
null,
|
null,
|
||||||
true
|
'svg'
|
||||||
)
|
)
|
||||||
expect(parent.innerHTML).toBe(content)
|
expect(parent.innerHTML).toBe(content)
|
||||||
expect(first).toBe(parent.firstChild)
|
expect(first).toBe(parent.firstChild)
|
||||||
|
@ -65,7 +75,7 @@ describe('runtime-dom: node-ops', () => {
|
||||||
content,
|
content,
|
||||||
parent,
|
parent,
|
||||||
anchor,
|
anchor,
|
||||||
true
|
'svg'
|
||||||
)
|
)
|
||||||
expect(parent.innerHTML).toBe(content + existing)
|
expect(parent.innerHTML).toBe(content + existing)
|
||||||
expect(first).toBe(parent.firstChild)
|
expect(first).toBe(parent.firstChild)
|
||||||
|
@ -88,7 +98,7 @@ describe('runtime-dom: node-ops', () => {
|
||||||
content,
|
content,
|
||||||
parent,
|
parent,
|
||||||
anchor,
|
anchor,
|
||||||
false,
|
undefined,
|
||||||
cached.firstChild,
|
cached.firstChild,
|
||||||
cached.lastChild
|
cached.lastChild
|
||||||
)
|
)
|
||||||
|
|
|
@ -4,15 +4,15 @@ import { xlinkNS } from '../src/modules/attrs'
|
||||||
describe('runtime-dom: attrs patching', () => {
|
describe('runtime-dom: attrs patching', () => {
|
||||||
test('xlink attributes', () => {
|
test('xlink attributes', () => {
|
||||||
const el = document.createElementNS('http://www.w3.org/2000/svg', 'use')
|
const el = document.createElementNS('http://www.w3.org/2000/svg', 'use')
|
||||||
patchProp(el, 'xlink:href', null, 'a', true)
|
patchProp(el, 'xlink:href', null, 'a', 'svg')
|
||||||
expect(el.getAttributeNS(xlinkNS, 'href')).toBe('a')
|
expect(el.getAttributeNS(xlinkNS, 'href')).toBe('a')
|
||||||
patchProp(el, 'xlink:href', 'a', null, true)
|
patchProp(el, 'xlink:href', 'a', null, 'svg')
|
||||||
expect(el.getAttributeNS(xlinkNS, 'href')).toBe(null)
|
expect(el.getAttributeNS(xlinkNS, 'href')).toBe(null)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('textContent attributes /w svg', () => {
|
test('textContent attributes /w svg', () => {
|
||||||
const el = document.createElementNS('http://www.w3.org/2000/svg', 'use')
|
const el = document.createElementNS('http://www.w3.org/2000/svg', 'use')
|
||||||
patchProp(el, 'textContent', null, 'foo', true)
|
patchProp(el, 'textContent', null, 'foo', 'svg')
|
||||||
expect(el.attributes.length).toBe(0)
|
expect(el.attributes.length).toBe(0)
|
||||||
expect(el.innerHTML).toBe('foo')
|
expect(el.innerHTML).toBe('foo')
|
||||||
})
|
})
|
||||||
|
|
|
@ -25,7 +25,7 @@ describe('runtime-dom: class patching', () => {
|
||||||
|
|
||||||
test('svg', () => {
|
test('svg', () => {
|
||||||
const el = document.createElementNS(svgNS, 'svg')
|
const el = document.createElementNS(svgNS, 'svg')
|
||||||
patchProp(el, 'class', null, 'foo', true)
|
patchProp(el, 'class', null, 'foo', 'svg')
|
||||||
expect(el.getAttribute('class')).toBe('foo')
|
expect(el.getAttribute('class')).toBe('foo')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -10,7 +10,8 @@ import {
|
||||||
RootHydrateFunction,
|
RootHydrateFunction,
|
||||||
isRuntimeOnly,
|
isRuntimeOnly,
|
||||||
DeprecationTypes,
|
DeprecationTypes,
|
||||||
compatUtils
|
compatUtils,
|
||||||
|
ElementNamespace
|
||||||
} from '@vue/runtime-core'
|
} from '@vue/runtime-core'
|
||||||
import { nodeOps } from './nodeOps'
|
import { nodeOps } from './nodeOps'
|
||||||
import { patchProp } from './patchProp'
|
import { patchProp } from './patchProp'
|
||||||
|
@ -21,7 +22,8 @@ import {
|
||||||
isHTMLTag,
|
isHTMLTag,
|
||||||
isSVGTag,
|
isSVGTag,
|
||||||
extend,
|
extend,
|
||||||
NOOP
|
NOOP,
|
||||||
|
isMathMLTag
|
||||||
} from '@vue/shared'
|
} from '@vue/shared'
|
||||||
|
|
||||||
declare module '@vue/reactivity' {
|
declare module '@vue/reactivity' {
|
||||||
|
@ -99,7 +101,7 @@ export const createApp = ((...args) => {
|
||||||
|
|
||||||
// clear content before mounting
|
// clear content before mounting
|
||||||
container.innerHTML = ''
|
container.innerHTML = ''
|
||||||
const proxy = mount(container, false, container instanceof SVGElement)
|
const proxy = mount(container, false, resolveRootNamespace(container))
|
||||||
if (container instanceof Element) {
|
if (container instanceof Element) {
|
||||||
container.removeAttribute('v-cloak')
|
container.removeAttribute('v-cloak')
|
||||||
container.setAttribute('data-v-app', '')
|
container.setAttribute('data-v-app', '')
|
||||||
|
@ -122,18 +124,30 @@ export const createSSRApp = ((...args) => {
|
||||||
app.mount = (containerOrSelector: Element | ShadowRoot | string): any => {
|
app.mount = (containerOrSelector: Element | ShadowRoot | string): any => {
|
||||||
const container = normalizeContainer(containerOrSelector)
|
const container = normalizeContainer(containerOrSelector)
|
||||||
if (container) {
|
if (container) {
|
||||||
return mount(container, true, container instanceof SVGElement)
|
return mount(container, true, resolveRootNamespace(container))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return app
|
return app
|
||||||
}) as CreateAppFunction<Element>
|
}) as CreateAppFunction<Element>
|
||||||
|
|
||||||
|
function resolveRootNamespace(container: Element): ElementNamespace {
|
||||||
|
if (container instanceof SVGElement) {
|
||||||
|
return 'svg'
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
typeof MathMLElement === 'function' &&
|
||||||
|
container instanceof MathMLElement
|
||||||
|
) {
|
||||||
|
return 'mathml'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function injectNativeTagCheck(app: App) {
|
function injectNativeTagCheck(app: App) {
|
||||||
// Inject `isNativeTag`
|
// Inject `isNativeTag`
|
||||||
// this is used for component name validation (dev only)
|
// this is used for component name validation (dev only)
|
||||||
Object.defineProperty(app.config, 'isNativeTag', {
|
Object.defineProperty(app.config, 'isNativeTag', {
|
||||||
value: (tag: string) => isHTMLTag(tag) || isSVGTag(tag),
|
value: (tag: string) => isHTMLTag(tag) || isSVGTag(tag) || isMathMLTag(tag),
|
||||||
writable: false
|
writable: false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { RendererOptions } from '@vue/runtime-core'
|
import { RendererOptions } from '@vue/runtime-core'
|
||||||
|
|
||||||
export const svgNS = 'http://www.w3.org/2000/svg'
|
export const svgNS = 'http://www.w3.org/2000/svg'
|
||||||
|
export const mathmlNS = 'http://www.w3.org/1998/Math/MathML'
|
||||||
|
|
||||||
const doc = (typeof document !== 'undefined' ? document : null) as Document
|
const doc = (typeof document !== 'undefined' ? document : null) as Document
|
||||||
|
|
||||||
|
@ -18,9 +19,12 @@ export const nodeOps: Omit<RendererOptions<Node, Element>, 'patchProp'> = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
createElement: (tag, isSVG, is, props): Element => {
|
createElement: (tag, namespace, is, props): Element => {
|
||||||
const el = isSVG
|
const el =
|
||||||
|
namespace === 'svg'
|
||||||
? doc.createElementNS(svgNS, tag)
|
? doc.createElementNS(svgNS, tag)
|
||||||
|
: namespace === 'mathml'
|
||||||
|
? doc.createElementNS(mathmlNS, tag)
|
||||||
: doc.createElement(tag, is ? { is } : undefined)
|
: doc.createElement(tag, is ? { is } : undefined)
|
||||||
|
|
||||||
if (tag === 'select' && props && props.multiple != null) {
|
if (tag === 'select' && props && props.multiple != null) {
|
||||||
|
@ -56,7 +60,7 @@ export const nodeOps: Omit<RendererOptions<Node, Element>, 'patchProp'> = {
|
||||||
// Reason: innerHTML.
|
// Reason: innerHTML.
|
||||||
// Static content here can only come from compiled templates.
|
// Static content here can only come from compiled templates.
|
||||||
// As long as the user only uses trusted templates, this is safe.
|
// As long as the user only uses trusted templates, this is safe.
|
||||||
insertStaticContent(content, parent, anchor, isSVG, start, end) {
|
insertStaticContent(content, parent, anchor, namespace, start, end) {
|
||||||
// <parent> before | first ... last | anchor </parent>
|
// <parent> before | first ... last | anchor </parent>
|
||||||
const before = anchor ? anchor.previousSibling : parent.lastChild
|
const before = anchor ? anchor.previousSibling : parent.lastChild
|
||||||
// #5308 can only take cached path if:
|
// #5308 can only take cached path if:
|
||||||
|
@ -70,10 +74,16 @@ export const nodeOps: Omit<RendererOptions<Node, Element>, 'patchProp'> = {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// fresh insert
|
// fresh insert
|
||||||
templateContainer.innerHTML = isSVG ? `<svg>${content}</svg>` : content
|
templateContainer.innerHTML =
|
||||||
|
namespace === 'svg'
|
||||||
|
? `<svg>${content}</svg>`
|
||||||
|
: namespace === 'mathml'
|
||||||
|
? `<math>${content}</math>`
|
||||||
|
: content
|
||||||
|
|
||||||
const template = templateContainer.content
|
const template = templateContainer.content
|
||||||
if (isSVG) {
|
if (namespace === 'svg' || namespace === 'mathml') {
|
||||||
// remove outer svg wrapper
|
// remove outer svg/math wrapper
|
||||||
const wrapper = template.firstChild!
|
const wrapper = template.firstChild!
|
||||||
while (wrapper.firstChild) {
|
while (wrapper.firstChild) {
|
||||||
template.appendChild(wrapper.firstChild)
|
template.appendChild(wrapper.firstChild)
|
||||||
|
|
|
@ -20,12 +20,13 @@ export const patchProp: DOMRendererOptions['patchProp'] = (
|
||||||
key,
|
key,
|
||||||
prevValue,
|
prevValue,
|
||||||
nextValue,
|
nextValue,
|
||||||
isSVG = false,
|
namespace,
|
||||||
prevChildren,
|
prevChildren,
|
||||||
parentComponent,
|
parentComponent,
|
||||||
parentSuspense,
|
parentSuspense,
|
||||||
unmountChildren
|
unmountChildren
|
||||||
) => {
|
) => {
|
||||||
|
const isSVG = namespace === 'svg'
|
||||||
if (key === 'class') {
|
if (key === 'class') {
|
||||||
patchClass(el, nextValue, isSVG)
|
patchClass(el, nextValue, isSVG)
|
||||||
} else if (key === 'style') {
|
} else if (key === 'style') {
|
||||||
|
|
|
@ -27,6 +27,13 @@ const SVG_TAGS =
|
||||||
'polygon,polyline,radialGradient,rect,set,solidcolor,stop,switch,symbol,' +
|
'polygon,polyline,radialGradient,rect,set,solidcolor,stop,switch,symbol,' +
|
||||||
'text,textPath,title,tspan,unknown,use,view'
|
'text,textPath,title,tspan,unknown,use,view'
|
||||||
|
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/MathML/Element
|
||||||
|
const MATH_TAGS =
|
||||||
|
'math,maction,annotation,annotation-xml,menclose,merror,mfenced,mfrac,mi,' +
|
||||||
|
'mmultiscripts,mn,mo,mover,mpadded,mphantom,mprescripts,mroot,mrow,ms,' +
|
||||||
|
'semantics,mspace,msqrt,mstyle,msub,msup,msubsup,mtable,mtd,mtext,mtr,' +
|
||||||
|
'munder,munderover'
|
||||||
|
|
||||||
const VOID_TAGS =
|
const VOID_TAGS =
|
||||||
'area,base,br,col,embed,hr,img,input,link,meta,param,source,track,wbr'
|
'area,base,br,col,embed,hr,img,input,link,meta,param,source,track,wbr'
|
||||||
|
|
||||||
|
@ -40,6 +47,11 @@ export const isHTMLTag = /*#__PURE__*/ makeMap(HTML_TAGS)
|
||||||
* Do NOT use in runtime code paths unless behind `__DEV__` flag.
|
* Do NOT use in runtime code paths unless behind `__DEV__` flag.
|
||||||
*/
|
*/
|
||||||
export const isSVGTag = /*#__PURE__*/ makeMap(SVG_TAGS)
|
export const isSVGTag = /*#__PURE__*/ makeMap(SVG_TAGS)
|
||||||
|
/**
|
||||||
|
* Compiler only.
|
||||||
|
* Do NOT use in runtime code paths unless behind `__DEV__` flag.
|
||||||
|
*/
|
||||||
|
export const isMathMLTag = /*#__PURE__*/ makeMap(MATH_TAGS)
|
||||||
/**
|
/**
|
||||||
* Compiler only.
|
* Compiler only.
|
||||||
* Do NOT use in runtime code paths unless behind `__DEV__` flag.
|
* Do NOT use in runtime code paths unless behind `__DEV__` flag.
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
// MathML logic is technically dom-specific, but the logic is placed in core
|
||||||
|
// because splitting it out of core would lead to unnecessary complexity in both
|
||||||
|
// the renderer and compiler implementations.
|
||||||
|
// Related files:
|
||||||
|
// - runtime-core/src/renderer.ts
|
||||||
|
// - compiler-core/src/transforms/transformElement.ts
|
||||||
|
|
||||||
|
import { vtcKey } from '../../runtime-dom/src/components/Transition'
|
||||||
|
import { render, h, ref, nextTick } from '../src'
|
||||||
|
|
||||||
|
describe('MathML support', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
document.body.innerHTML = ''
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should mount elements with correct html namespace', () => {
|
||||||
|
const root = document.createElement('div')
|
||||||
|
document.body.appendChild(root)
|
||||||
|
const App = {
|
||||||
|
template: `
|
||||||
|
<math display="block" id="e0">
|
||||||
|
<semantics id="e1">
|
||||||
|
<mrow id="e2">
|
||||||
|
<msup>
|
||||||
|
<mi>x</mi>
|
||||||
|
<mn>2</mn>
|
||||||
|
</msup>
|
||||||
|
<mo>+</mo>
|
||||||
|
<mi>y</mi>
|
||||||
|
</mrow>
|
||||||
|
|
||||||
|
<annotation-xml encoding="text/html" id="e3">
|
||||||
|
<div id="e4" />
|
||||||
|
<svg id="e5" />
|
||||||
|
</annotation-xml>
|
||||||
|
</semantics>
|
||||||
|
</math>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
render(h(App), root)
|
||||||
|
const e0 = document.getElementById('e0')!
|
||||||
|
expect(e0.namespaceURI).toMatch('Math')
|
||||||
|
expect(e0.querySelector('#e1')!.namespaceURI).toMatch('Math')
|
||||||
|
expect(e0.querySelector('#e2')!.namespaceURI).toMatch('Math')
|
||||||
|
expect(e0.querySelector('#e3')!.namespaceURI).toMatch('Math')
|
||||||
|
expect(e0.querySelector('#e4')!.namespaceURI).toMatch('xhtml')
|
||||||
|
expect(e0.querySelector('#e5')!.namespaceURI).toMatch('svg')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should patch elements with correct namespaces', async () => {
|
||||||
|
const root = document.createElement('div')
|
||||||
|
document.body.appendChild(root)
|
||||||
|
const cls = ref('foo')
|
||||||
|
const App = {
|
||||||
|
setup: () => ({ cls }),
|
||||||
|
template: `
|
||||||
|
<div>
|
||||||
|
<math id="f1" :class="cls">
|
||||||
|
<annotation encoding="text/html">
|
||||||
|
<div id="f2" :class="cls"/>
|
||||||
|
</annotation>
|
||||||
|
</math>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
render(h(App), root)
|
||||||
|
const f1 = document.querySelector('#f1')!
|
||||||
|
const f2 = document.querySelector('#f2')!
|
||||||
|
expect(f1.getAttribute('class')).toBe('foo')
|
||||||
|
expect(f2.className).toBe('foo')
|
||||||
|
|
||||||
|
// set a transition class on the <div> - which is only respected on non-svg
|
||||||
|
// patches
|
||||||
|
;(f2 as any)[vtcKey] = ['baz']
|
||||||
|
cls.value = 'bar'
|
||||||
|
await nextTick()
|
||||||
|
expect(f1.getAttribute('class')).toBe('bar')
|
||||||
|
expect(f2.className).toBe('bar baz')
|
||||||
|
})
|
||||||
|
})
|
|
@ -9,7 +9,11 @@ import { vtcKey } from '../../runtime-dom/src/components/Transition'
|
||||||
import { render, h, ref, nextTick } from '../src'
|
import { render, h, ref, nextTick } from '../src'
|
||||||
|
|
||||||
describe('SVG support', () => {
|
describe('SVG support', () => {
|
||||||
test('should mount elements with correct namespaces', () => {
|
afterEach(() => {
|
||||||
|
document.body.innerHTML = ''
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should mount elements with correct html namespace', () => {
|
||||||
const root = document.createElement('div')
|
const root = document.createElement('div')
|
||||||
document.body.appendChild(root)
|
document.body.appendChild(root)
|
||||||
const App = {
|
const App = {
|
||||||
|
@ -18,6 +22,8 @@ describe('SVG support', () => {
|
||||||
<svg id="e1">
|
<svg id="e1">
|
||||||
<foreignObject id="e2">
|
<foreignObject id="e2">
|
||||||
<div id="e3"/>
|
<div id="e3"/>
|
||||||
|
<svg id="e4"/>
|
||||||
|
<math id="e5"/>
|
||||||
</foreignObject>
|
</foreignObject>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
@ -29,6 +35,8 @@ describe('SVG support', () => {
|
||||||
expect(e0.querySelector('#e1')!.namespaceURI).toMatch('svg')
|
expect(e0.querySelector('#e1')!.namespaceURI).toMatch('svg')
|
||||||
expect(e0.querySelector('#e2')!.namespaceURI).toMatch('svg')
|
expect(e0.querySelector('#e2')!.namespaceURI).toMatch('svg')
|
||||||
expect(e0.querySelector('#e3')!.namespaceURI).toMatch('xhtml')
|
expect(e0.querySelector('#e3')!.namespaceURI).toMatch('xhtml')
|
||||||
|
expect(e0.querySelector('#e4')!.namespaceURI).toMatch('svg')
|
||||||
|
expect(e0.querySelector('#e5')!.namespaceURI).toMatch('Math')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('should patch elements with correct namespaces', async () => {
|
test('should patch elements with correct namespaces', async () => {
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import { type SpyInstance } from 'vitest'
|
import { type SpyInstance } from 'vitest'
|
||||||
|
|
||||||
|
vi.stubGlobal('MathMLElement', class MathMLElement {})
|
||||||
|
|
||||||
expect.extend({
|
expect.extend({
|
||||||
toHaveBeenWarned(received: string) {
|
toHaveBeenWarned(received: string) {
|
||||||
asserted.add(received)
|
asserted.add(received)
|
||||||
|
|
Loading…
Reference in New Issue