refactor: cache constant check result on transform context

also fix edge case for missed createVNode import on static svg nodes
This commit is contained in:
Evan You 2020-12-03 11:10:40 -05:00
parent ad4d3915d3
commit a8352506f6
4 changed files with 28 additions and 29 deletions

View File

@ -111,6 +111,7 @@ export interface TransformContext
removeIdentifiers(exp: ExpressionNode | string): void removeIdentifiers(exp: ExpressionNode | string): void
hoist(exp: JSChildNode): SimpleExpressionNode hoist(exp: JSChildNode): SimpleExpressionNode
cache<T extends JSChildNode>(exp: T, isVNode?: boolean): CacheExpression | T cache<T extends JSChildNode>(exp: T, isVNode?: boolean): CacheExpression | T
constantCache: Map<TemplateChildNode, ConstantTypes>
} }
export function createTransformContext( export function createTransformContext(
@ -163,6 +164,7 @@ export function createTransformContext(
directives: new Set(), directives: new Set(),
hoists: [], hoists: [],
imports: new Set(), imports: new Set(),
constantCache: new Map(),
temps: 0, temps: 0,
cached: 0, cached: 0,
identifiers: Object.create(null), identifiers: Object.create(null),

View File

@ -14,12 +14,12 @@ import {
import { TransformContext } from '../transform' import { TransformContext } from '../transform'
import { PatchFlags, isString, isSymbol } from '@vue/shared' import { PatchFlags, isString, isSymbol } from '@vue/shared'
import { isSlotOutlet } from '../utils' import { isSlotOutlet } from '../utils'
import { CREATE_VNODE } from '../runtimeHelpers'
export function hoistStatic(root: RootNode, context: TransformContext) { export function hoistStatic(root: RootNode, context: TransformContext) {
walk( walk(
root, root,
context, context,
new Map(),
// Root node is unfortunately non-hoistable due to potential parent // Root node is unfortunately non-hoistable due to potential parent
// fallthrough attributes. // fallthrough attributes.
isSingleElementRoot(root, root.children[0]) isSingleElementRoot(root, root.children[0])
@ -41,7 +41,6 @@ export function isSingleElementRoot(
function walk( function walk(
node: ParentNode, node: ParentNode,
context: TransformContext, context: TransformContext,
resultCache: Map<TemplateChildNode, ConstantTypes>,
doNotHoistNode: boolean = false doNotHoistNode: boolean = false
) { ) {
let hasHoistedNode = false let hasHoistedNode = false
@ -65,7 +64,7 @@ function walk(
) { ) {
const constantType = doNotHoistNode const constantType = doNotHoistNode
? ConstantTypes.NOT_CONSTANT ? ConstantTypes.NOT_CONSTANT
: getConstantType(child, resultCache) : getConstantType(child, context)
if (constantType > ConstantTypes.NOT_CONSTANT) { if (constantType > ConstantTypes.NOT_CONSTANT) {
if (constantType < ConstantTypes.CAN_STRINGIFY) { if (constantType < ConstantTypes.CAN_STRINGIFY) {
canStringify = false canStringify = false
@ -87,7 +86,7 @@ function walk(
(!flag || (!flag ||
flag === PatchFlags.NEED_PATCH || flag === PatchFlags.NEED_PATCH ||
flag === PatchFlags.TEXT) && flag === PatchFlags.TEXT) &&
getGeneratedPropsConstantType(child, resultCache) >= getGeneratedPropsConstantType(child, context) >=
ConstantTypes.CAN_HOIST ConstantTypes.CAN_HOIST
) { ) {
const props = getNodeProps(child) const props = getNodeProps(child)
@ -98,7 +97,7 @@ function walk(
} }
} }
} else if (child.type === NodeTypes.TEXT_CALL) { } else if (child.type === NodeTypes.TEXT_CALL) {
const contentType = getConstantType(child.content, resultCache) const contentType = getConstantType(child.content, context)
if (contentType > 0) { if (contentType > 0) {
if (contentType < ConstantTypes.CAN_STRINGIFY) { if (contentType < ConstantTypes.CAN_STRINGIFY) {
canStringify = false canStringify = false
@ -112,17 +111,16 @@ function walk(
// walk further // walk further
if (child.type === NodeTypes.ELEMENT) { if (child.type === NodeTypes.ELEMENT) {
walk(child, context, resultCache) walk(child, context)
} else if (child.type === NodeTypes.FOR) { } else if (child.type === NodeTypes.FOR) {
// Do not hoist v-for single child because it has to be a block // Do not hoist v-for single child because it has to be a block
walk(child, context, resultCache, child.children.length === 1) walk(child, context, child.children.length === 1)
} else if (child.type === NodeTypes.IF) { } else if (child.type === NodeTypes.IF) {
for (let i = 0; i < child.branches.length; i++) { for (let i = 0; i < child.branches.length; i++) {
// Do not hoist v-if single child because it has to be a block // Do not hoist v-if single child because it has to be a block
walk( walk(
child.branches[i], child.branches[i],
context, context,
resultCache,
child.branches[i].children.length === 1 child.branches[i].children.length === 1
) )
} }
@ -136,14 +134,15 @@ function walk(
export function getConstantType( export function getConstantType(
node: TemplateChildNode | SimpleExpressionNode, node: TemplateChildNode | SimpleExpressionNode,
resultCache: Map<TemplateChildNode, ConstantTypes> = new Map() context: TransformContext
): ConstantTypes { ): ConstantTypes {
const { constantCache } = context
switch (node.type) { switch (node.type) {
case NodeTypes.ELEMENT: case NodeTypes.ELEMENT:
if (node.tagType !== ElementTypes.ELEMENT) { if (node.tagType !== ElementTypes.ELEMENT) {
return ConstantTypes.NOT_CONSTANT return ConstantTypes.NOT_CONSTANT
} }
const cached = resultCache.get(node) const cached = constantCache.get(node)
if (cached !== undefined) { if (cached !== undefined) {
return cached return cached
} }
@ -161,12 +160,9 @@ export function getConstantType(
// non-hoistable expressions that refers to scope variables, e.g. compiler // non-hoistable expressions that refers to scope variables, e.g. compiler
// injected keys or cached event handlers. Therefore we need to always // injected keys or cached event handlers. Therefore we need to always
// check the codegenNode's props to be sure. // check the codegenNode's props to be sure.
const generatedPropsType = getGeneratedPropsConstantType( const generatedPropsType = getGeneratedPropsConstantType(node, context)
node,
resultCache
)
if (generatedPropsType === ConstantTypes.NOT_CONSTANT) { if (generatedPropsType === ConstantTypes.NOT_CONSTANT) {
resultCache.set(node, ConstantTypes.NOT_CONSTANT) constantCache.set(node, ConstantTypes.NOT_CONSTANT)
return ConstantTypes.NOT_CONSTANT return ConstantTypes.NOT_CONSTANT
} }
if (generatedPropsType < returnType) { if (generatedPropsType < returnType) {
@ -175,9 +171,9 @@ export function getConstantType(
// 2. its children. // 2. its children.
for (let i = 0; i < node.children.length; i++) { for (let i = 0; i < node.children.length; i++) {
const childType = getConstantType(node.children[i], resultCache) const childType = getConstantType(node.children[i], context)
if (childType === ConstantTypes.NOT_CONSTANT) { if (childType === ConstantTypes.NOT_CONSTANT) {
resultCache.set(node, ConstantTypes.NOT_CONSTANT) constantCache.set(node, ConstantTypes.NOT_CONSTANT)
return ConstantTypes.NOT_CONSTANT return ConstantTypes.NOT_CONSTANT
} }
if (childType < returnType) { if (childType < returnType) {
@ -193,9 +189,9 @@ export function getConstantType(
for (let i = 0; i < node.props.length; i++) { for (let i = 0; i < node.props.length; i++) {
const p = node.props[i] const p = node.props[i]
if (p.type === NodeTypes.DIRECTIVE && p.name === 'bind' && p.exp) { if (p.type === NodeTypes.DIRECTIVE && p.name === 'bind' && p.exp) {
const expType = getConstantType(p.exp, resultCache) const expType = getConstantType(p.exp, context)
if (expType === ConstantTypes.NOT_CONSTANT) { if (expType === ConstantTypes.NOT_CONSTANT) {
resultCache.set(node, ConstantTypes.NOT_CONSTANT) constantCache.set(node, ConstantTypes.NOT_CONSTANT)
return ConstantTypes.NOT_CONSTANT return ConstantTypes.NOT_CONSTANT
} }
if (expType < returnType) { if (expType < returnType) {
@ -210,12 +206,13 @@ export function getConstantType(
// nested updates. // nested updates.
if (codegenNode.isBlock) { if (codegenNode.isBlock) {
codegenNode.isBlock = false codegenNode.isBlock = false
context.helper(CREATE_VNODE)
} }
resultCache.set(node, returnType) constantCache.set(node, returnType)
return returnType return returnType
} else { } else {
resultCache.set(node, ConstantTypes.NOT_CONSTANT) constantCache.set(node, ConstantTypes.NOT_CONSTANT)
return ConstantTypes.NOT_CONSTANT return ConstantTypes.NOT_CONSTANT
} }
case NodeTypes.TEXT: case NodeTypes.TEXT:
@ -227,7 +224,7 @@ export function getConstantType(
return ConstantTypes.NOT_CONSTANT return ConstantTypes.NOT_CONSTANT
case NodeTypes.INTERPOLATION: case NodeTypes.INTERPOLATION:
case NodeTypes.TEXT_CALL: case NodeTypes.TEXT_CALL:
return getConstantType(node.content, resultCache) return getConstantType(node.content, context)
case NodeTypes.SIMPLE_EXPRESSION: case NodeTypes.SIMPLE_EXPRESSION:
return node.constType return node.constType
case NodeTypes.COMPOUND_EXPRESSION: case NodeTypes.COMPOUND_EXPRESSION:
@ -237,7 +234,7 @@ export function getConstantType(
if (isString(child) || isSymbol(child)) { if (isString(child) || isSymbol(child)) {
continue continue
} }
const childType = getConstantType(child, resultCache) const childType = getConstantType(child, context)
if (childType === ConstantTypes.NOT_CONSTANT) { if (childType === ConstantTypes.NOT_CONSTANT) {
return ConstantTypes.NOT_CONSTANT return ConstantTypes.NOT_CONSTANT
} else if (childType < returnType) { } else if (childType < returnType) {
@ -256,7 +253,7 @@ export function getConstantType(
function getGeneratedPropsConstantType( function getGeneratedPropsConstantType(
node: PlainElementNode, node: PlainElementNode,
resultCache: Map<TemplateChildNode, ConstantTypes> context: TransformContext
): ConstantTypes { ): ConstantTypes {
let returnType = ConstantTypes.CAN_STRINGIFY let returnType = ConstantTypes.CAN_STRINGIFY
const props = getNodeProps(node) const props = getNodeProps(node)
@ -264,7 +261,7 @@ function getGeneratedPropsConstantType(
const { properties } = props const { properties } = props
for (let i = 0; i < properties.length; i++) { for (let i = 0; i < properties.length; i++) {
const { key, value } = properties[i] const { key, value } = properties[i]
const keyType = getConstantType(key, resultCache) const keyType = getConstantType(key, context)
if (keyType === ConstantTypes.NOT_CONSTANT) { if (keyType === ConstantTypes.NOT_CONSTANT) {
return keyType return keyType
} }
@ -274,7 +271,7 @@ function getGeneratedPropsConstantType(
if (value.type !== NodeTypes.SIMPLE_EXPRESSION) { if (value.type !== NodeTypes.SIMPLE_EXPRESSION) {
return ConstantTypes.NOT_CONSTANT return ConstantTypes.NOT_CONSTANT
} }
const valueType = getConstantType(value, resultCache) const valueType = getConstantType(value, context)
if (valueType === ConstantTypes.NOT_CONSTANT) { if (valueType === ConstantTypes.NOT_CONSTANT) {
return valueType return valueType
} }

View File

@ -168,7 +168,7 @@ export const transformElement: NodeTransform = (node, context) => {
type === NodeTypes.COMPOUND_EXPRESSION type === NodeTypes.COMPOUND_EXPRESSION
if ( if (
hasDynamicTextChild && hasDynamicTextChild &&
getConstantType(child) === ConstantTypes.NOT_CONSTANT getConstantType(child, context) === ConstantTypes.NOT_CONSTANT
) { ) {
patchFlag |= PatchFlags.TEXT patchFlag |= PatchFlags.TEXT
} }
@ -373,7 +373,7 @@ export function buildProps(
value.type === NodeTypes.JS_CACHE_EXPRESSION || value.type === NodeTypes.JS_CACHE_EXPRESSION ||
((value.type === NodeTypes.SIMPLE_EXPRESSION || ((value.type === NodeTypes.SIMPLE_EXPRESSION ||
value.type === NodeTypes.COMPOUND_EXPRESSION) && value.type === NodeTypes.COMPOUND_EXPRESSION) &&
getConstantType(value) > 0) getConstantType(value, context) > 0)
) { ) {
// skip if the prop is a cached handler or has constant value // skip if the prop is a cached handler or has constant value
return return

View File

@ -82,7 +82,7 @@ export const transformText: NodeTransform = (node, context) => {
// mark dynamic text with flag so it gets patched inside a block // mark dynamic text with flag so it gets patched inside a block
if ( if (
!context.ssr && !context.ssr &&
getConstantType(child) === ConstantTypes.NOT_CONSTANT getConstantType(child, context) === ConstantTypes.NOT_CONSTANT
) { ) {
callArgs.push( callArgs.push(
PatchFlags.TEXT + PatchFlags.TEXT +