feat(compiler-dom/runtime-dom): stringify eligible static trees
This commit is contained in:
@@ -18,6 +18,7 @@ import { transformModel } from './transforms/vModel'
|
||||
import { transformOn } from './transforms/vOn'
|
||||
import { transformShow } from './transforms/vShow'
|
||||
import { warnTransitionChildren } from './transforms/warnTransitionChildren'
|
||||
import { stringifyStatic } from './stringifyStatic'
|
||||
|
||||
export const parserOptions = __BROWSER__
|
||||
? parserOptionsMinimal
|
||||
@@ -41,17 +42,16 @@ export function compile(
|
||||
template: string,
|
||||
options: CompilerOptions = {}
|
||||
): CodegenResult {
|
||||
const result = baseCompile(template, {
|
||||
return baseCompile(template, {
|
||||
...parserOptions,
|
||||
...options,
|
||||
nodeTransforms: [...DOMNodeTransforms, ...(options.nodeTransforms || [])],
|
||||
directiveTransforms: {
|
||||
...DOMDirectiveTransforms,
|
||||
...(options.directiveTransforms || {})
|
||||
}
|
||||
},
|
||||
transformHoist: __BROWSER__ ? null : stringifyStatic
|
||||
})
|
||||
// debugger
|
||||
return result
|
||||
}
|
||||
|
||||
export function parse(template: string, options: ParserOptions = {}): RootNode {
|
||||
|
||||
116
packages/compiler-dom/src/stringifyStatic.ts
Normal file
116
packages/compiler-dom/src/stringifyStatic.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
import {
|
||||
NodeTypes,
|
||||
ElementNode,
|
||||
TransformContext,
|
||||
TemplateChildNode,
|
||||
SimpleExpressionNode,
|
||||
createCallExpression,
|
||||
HoistTransform,
|
||||
CREATE_STATIC
|
||||
} from '@vue/compiler-core'
|
||||
import { isVoidTag, isString, isSymbol, escapeHtml } from '@vue/shared'
|
||||
|
||||
// Turn eligible hoisted static trees into stringied static nodes, e.g.
|
||||
// const _hoisted_1 = createStaticVNode(`<div class="foo">bar</div>`)
|
||||
export const stringifyStatic: HoistTransform = (node, context) => {
|
||||
if (shouldOptimize(node)) {
|
||||
return createCallExpression(context.helper(CREATE_STATIC), [
|
||||
JSON.stringify(stringifyElement(node, context))
|
||||
])
|
||||
} else {
|
||||
return node.codegenNode!
|
||||
}
|
||||
}
|
||||
|
||||
// Opt-in heuristics based on:
|
||||
// 1. number of elements with attributes > 5.
|
||||
// 2. OR: number of total nodes > 20
|
||||
// For some simple trees, the performance can actually be worse.
|
||||
// it is only worth it when the tree is complex enough
|
||||
// (e.g. big piece of static content)
|
||||
function shouldOptimize(node: ElementNode): boolean {
|
||||
let bindingThreshold = 5
|
||||
let nodeThreshold = 20
|
||||
|
||||
function walk(node: ElementNode) {
|
||||
for (let i = 0; i < node.children.length; i++) {
|
||||
if (--nodeThreshold === 0) {
|
||||
return true
|
||||
}
|
||||
const child = node.children[i]
|
||||
if (child.type === NodeTypes.ELEMENT) {
|
||||
if (child.props.length > 0 && --bindingThreshold === 0) {
|
||||
return true
|
||||
}
|
||||
if (walk(child)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
return walk(node)
|
||||
}
|
||||
|
||||
function stringifyElement(
|
||||
node: ElementNode,
|
||||
context: TransformContext
|
||||
): string {
|
||||
let res = `<${node.tag}`
|
||||
for (let i = 0; i < node.props.length; i++) {
|
||||
const p = node.props[i]
|
||||
if (p.type === NodeTypes.ATTRIBUTE) {
|
||||
res += ` ${p.name}`
|
||||
if (p.value) {
|
||||
res += `="${p.value.content}"`
|
||||
}
|
||||
} else if (p.type === NodeTypes.DIRECTIVE && p.name === 'bind') {
|
||||
// constant v-bind, e.g. :foo="1"
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
if (context.scopeId) {
|
||||
res += ` ${context.scopeId}`
|
||||
}
|
||||
res += `>`
|
||||
for (let i = 0; i < node.children.length; i++) {
|
||||
res += stringifyNode(node.children[i], context)
|
||||
}
|
||||
if (!isVoidTag(node.tag)) {
|
||||
res += `</${node.tag}>`
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
function stringifyNode(
|
||||
node: string | TemplateChildNode,
|
||||
context: TransformContext
|
||||
): string {
|
||||
if (isString(node)) {
|
||||
return node
|
||||
}
|
||||
if (isSymbol(node)) {
|
||||
return ``
|
||||
}
|
||||
switch (node.type) {
|
||||
case NodeTypes.ELEMENT:
|
||||
return stringifyElement(node, context)
|
||||
case NodeTypes.TEXT:
|
||||
return escapeHtml(node.content)
|
||||
case NodeTypes.COMMENT:
|
||||
return `<!--${escapeHtml(node.content)}-->`
|
||||
case NodeTypes.INTERPOLATION:
|
||||
// constants
|
||||
// TODO check eval
|
||||
return (node.content as SimpleExpressionNode).content
|
||||
case NodeTypes.COMPOUND_EXPRESSION:
|
||||
// TODO proper handling
|
||||
return node.children.map((c: any) => stringifyNode(c, context)).join('')
|
||||
case NodeTypes.TEXT_CALL:
|
||||
return stringifyNode(node.content, context)
|
||||
default:
|
||||
// static trees will not contain if/for nodes
|
||||
return ''
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user