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