feat(runtime-vapor): support svg and MathML

This commit is contained in:
daiwei 2025-07-25 16:10:29 +08:00
parent f70f9d1a6b
commit 40b1e1e5de
5 changed files with 37 additions and 12 deletions

View File

@ -7,15 +7,15 @@ import { type CodeFragment, NEWLINE, buildCodeFragment, genCall } from './utils'
export function genTemplates(
templates: string[],
rootIndex: number | undefined,
{ helper }: CodegenContext,
{ helper, ir: { templateNS } }: CodegenContext,
): string {
return templates
.map(
(template, i) =>
`const t${i} = ${helper('template')}(${JSON.stringify(
template,
)}${i === rootIndex ? ', true' : ''})\n`,
)
.map((template, i) => {
const ns = templateNS.get(template)
return `const t${i} = ${helper('template')}(${JSON.stringify(
template,
)}${i === rootIndex ? ', true' : ns ? ', false' : ''}${ns ? `, ${ns}` : ''})\n`
})
.join('')
}

View File

@ -60,6 +60,7 @@ export interface RootIRNode {
node: RootNode
source: string
template: string[]
templateNS: Map<string, number>
rootTemplateIndex?: number
component: Set<string>
directive: Set<string>

View File

@ -6,6 +6,7 @@ import {
type ElementNode,
ElementTypes,
NodeTypes,
type PlainElementNode,
type RootNode,
type SimpleExpressionNode,
type TemplateChildNode,
@ -73,6 +74,7 @@ export class TransformContext<T extends AllNode = AllNode> {
>
template: string = ''
templateNS: Map<string, number> = new Map<string, number>()
childrenTemplate: (string | null)[] = []
dynamic: IRDynamicInfo = this.ir.block.dynamic
@ -98,10 +100,12 @@ export class TransformContext<T extends AllNode = AllNode> {
}
enterBlock(ir: BlockIRNode, isVFor: boolean = false): () => void {
const { block, template, dynamic, childrenTemplate, slots } = this
const { block, template, templateNS, dynamic, childrenTemplate, slots } =
this
this.block = ir
this.dynamic = ir.dynamic
this.template = ''
this.templateNS = new Map<string, number>()
this.childrenTemplate = []
this.slots = []
isVFor && this.inVFor++
@ -110,6 +114,7 @@ export class TransformContext<T extends AllNode = AllNode> {
this.registerTemplate()
this.block = block
this.template = template
this.templateNS = templateNS
this.dynamic = dynamic
this.childrenTemplate = childrenTemplate
this.slots = slots
@ -130,6 +135,7 @@ export class TransformContext<T extends AllNode = AllNode> {
)
if (existing !== -1) return existing
this.ir.template.push(content)
this.ir.templateNS.set(content, (this.node as PlainElementNode).ns)
return this.ir.template.length - 1
}
registerTemplate(): number {
@ -215,6 +221,7 @@ export function transform(
node,
source: node.source,
template: [],
templateNS: new Map<string, number>(),
component: new Set(),
directive: new Set(),
block: newBlock(node),

View File

@ -348,3 +348,7 @@ export {
vModelSelectInit,
vModelSetSelected,
} from './directives/vModel'
/**
* @internal
*/
export { svgNS, mathmlNS } from './nodeOps'

View File

@ -1,10 +1,13 @@
import { mathmlNS, svgNS } from '@vue/runtime-dom'
import { adoptTemplate, currentHydrationNode, isHydrating } from './hydration'
import { child, createTextNode } from './node'
let t: HTMLTemplateElement
let st: HTMLTemplateElement
let mt: HTMLTemplateElement
/*! #__NO_SIDE_EFFECTS__ */
export function template(html: string, root?: boolean) {
export function template(html: string, root?: boolean, ns?: number) {
let node: Node
return (): Node & { $root?: true } => {
if (isHydrating) {
@ -19,9 +22,19 @@ export function template(html: string, root?: boolean) {
return createTextNode(html)
}
if (!node) {
t = t || document.createElement('template')
t.innerHTML = html
node = child(t.content)
if (!ns) {
t = t || document.createElement('template')
t.innerHTML = html
node = child(t.content)
} else if (ns === 1) {
st = st || document.createElementNS(svgNS, 'template')
st.innerHTML = html
node = child(st)
} else {
mt = mt || document.createElementNS(mathmlNS, 'template')
mt.innerHTML = html
node = child(mt)
}
}
const ret = node.cloneNode(true)
if (root) (ret as any).$root = true