feat(compiler-core): more hoisting optimizations (#276)
This commit is contained in:
@@ -172,6 +172,7 @@ export interface SimpleExpressionNode extends Node {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION
|
||||
content: string
|
||||
isStatic: boolean
|
||||
isConstant: boolean
|
||||
// an expression parsed as the params of a function will track
|
||||
// the identifiers declared inside the function body.
|
||||
identifiers?: string[]
|
||||
@@ -501,11 +502,13 @@ export function createObjectProperty(
|
||||
export function createSimpleExpression(
|
||||
content: SimpleExpressionNode['content'],
|
||||
isStatic: SimpleExpressionNode['isStatic'],
|
||||
loc: SourceLocation = locStub
|
||||
loc: SourceLocation = locStub,
|
||||
isConstant: boolean = false
|
||||
): SimpleExpressionNode {
|
||||
return {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
loc,
|
||||
isConstant,
|
||||
content,
|
||||
isStatic
|
||||
}
|
||||
|
||||
@@ -564,9 +564,12 @@ function parseAttribute(
|
||||
)
|
||||
let content = match[2]
|
||||
let isStatic = true
|
||||
// Non-dynamic arg is a constant.
|
||||
let isConstant = true
|
||||
|
||||
if (content.startsWith('[')) {
|
||||
isStatic = false
|
||||
isConstant = false
|
||||
|
||||
if (!content.endsWith(']')) {
|
||||
emitError(
|
||||
@@ -582,6 +585,7 @@ function parseAttribute(
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content,
|
||||
isStatic,
|
||||
isConstant,
|
||||
loc
|
||||
}
|
||||
}
|
||||
@@ -607,6 +611,8 @@ function parseAttribute(
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: value.content,
|
||||
isStatic: false,
|
||||
// Set `isConstant` to false by default and will decide in transformExpression
|
||||
isConstant: false,
|
||||
loc: value.loc
|
||||
},
|
||||
arg,
|
||||
@@ -713,6 +719,8 @@ function parseInterpolation(
|
||||
content: {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
isStatic: false,
|
||||
// Set `isConstant` to false by default and will decide in transformExpression
|
||||
isConstant: false,
|
||||
content,
|
||||
loc: getSelection(context, innerStart, innerEnd)
|
||||
},
|
||||
|
||||
@@ -2,6 +2,7 @@ import {
|
||||
RootNode,
|
||||
NodeTypes,
|
||||
TemplateChildNode,
|
||||
SimpleExpressionNode,
|
||||
ElementTypes,
|
||||
ElementCodegenNode,
|
||||
PlainElementNode,
|
||||
@@ -11,7 +12,7 @@ import {
|
||||
} from '../ast'
|
||||
import { TransformContext } from '../transform'
|
||||
import { APPLY_DIRECTIVES } from '../runtimeHelpers'
|
||||
import { PatchFlags } from '@vue/shared'
|
||||
import { PatchFlags, isString, isSymbol } from '@vue/shared'
|
||||
import { isSlotOutlet, findProp } from '../utils'
|
||||
|
||||
function hasDynamicKey(node: ElementNode) {
|
||||
@@ -107,7 +108,7 @@ function getPatchFlag(node: PlainElementNode): number | undefined {
|
||||
}
|
||||
|
||||
function isStaticNode(
|
||||
node: TemplateChildNode,
|
||||
node: TemplateChildNode | SimpleExpressionNode,
|
||||
resultCache: Map<TemplateChildNode, boolean>
|
||||
): boolean {
|
||||
switch (node.type) {
|
||||
@@ -119,7 +120,7 @@ function isStaticNode(
|
||||
return resultCache.get(node) as boolean
|
||||
}
|
||||
const flag = getPatchFlag(node)
|
||||
if (!flag) {
|
||||
if (!flag || flag === PatchFlags.TEXT) {
|
||||
// element self is static. check its children.
|
||||
for (let i = 0; i < node.children.length; i++) {
|
||||
if (!isStaticNode(node.children[i], resultCache)) {
|
||||
@@ -137,9 +138,17 @@ function isStaticNode(
|
||||
return true
|
||||
case NodeTypes.IF:
|
||||
case NodeTypes.FOR:
|
||||
case NodeTypes.INTERPOLATION:
|
||||
case NodeTypes.COMPOUND_EXPRESSION:
|
||||
return false
|
||||
case NodeTypes.INTERPOLATION:
|
||||
return isStaticNode(node.content, resultCache)
|
||||
case NodeTypes.SIMPLE_EXPRESSION:
|
||||
return node.isConstant
|
||||
case NodeTypes.COMPOUND_EXPRESSION:
|
||||
return node.children.every(child => {
|
||||
return (
|
||||
isString(child) || isSymbol(child) || isStaticNode(child, resultCache)
|
||||
)
|
||||
})
|
||||
default:
|
||||
if (__DEV__) {
|
||||
const exhaustiveCheck: never = node
|
||||
|
||||
@@ -190,7 +190,12 @@ export function buildProps(
|
||||
|
||||
const analyzePatchFlag = ({ key, value }: Property) => {
|
||||
if (key.type === NodeTypes.SIMPLE_EXPRESSION && key.isStatic) {
|
||||
if (value.type !== NodeTypes.SIMPLE_EXPRESSION || !value.isStatic) {
|
||||
if (
|
||||
value.type !== NodeTypes.SIMPLE_EXPRESSION ||
|
||||
// E.g: <p :foo="1 + 2" />.
|
||||
// Do not add prop `foo` to `dynamicPropNames`.
|
||||
(!value.isStatic && !value.isConstant)
|
||||
) {
|
||||
const name = key.content
|
||||
if (name === 'ref') {
|
||||
hasRef = true
|
||||
|
||||
@@ -61,6 +61,7 @@ export const transformExpression: NodeTransform = (node, context) => {
|
||||
|
||||
interface PrefixMeta {
|
||||
prefix?: string
|
||||
isConstant: boolean
|
||||
start: number
|
||||
end: number
|
||||
scopeIds?: Set<string>
|
||||
@@ -108,6 +109,7 @@ export function processExpression(
|
||||
const ids: (Identifier & PrefixMeta)[] = []
|
||||
const knownIds = Object.create(context.identifiers)
|
||||
|
||||
let isConstant = true
|
||||
// walk the AST and look for identifiers that need to be prefixed with `_ctx.`.
|
||||
walkJS(ast, {
|
||||
enter(node: Node & PrefixMeta, parent) {
|
||||
@@ -120,8 +122,15 @@ export function processExpression(
|
||||
node.prefix = `${node.name}: `
|
||||
}
|
||||
node.name = `_ctx.${node.name}`
|
||||
node.isConstant = false
|
||||
isConstant = false
|
||||
ids.push(node)
|
||||
} else if (!isStaticPropertyKey(node, parent)) {
|
||||
// This means this identifier is pointing to a scope variable (a v-for alias, or a v-slot prop)
|
||||
// which is also dynamic and cannot be hoisted.
|
||||
node.isConstant = !(
|
||||
knownIds[node.name] && shouldPrefix(node, parent)
|
||||
)
|
||||
// also generate sub-expressions for other identifiers for better
|
||||
// source map support. (except for property keys which are static)
|
||||
ids.push(node)
|
||||
@@ -190,11 +199,16 @@ export function processExpression(
|
||||
}
|
||||
const source = rawExp.slice(start, end)
|
||||
children.push(
|
||||
createSimpleExpression(id.name, false, {
|
||||
source,
|
||||
start: advancePositionWithClone(node.loc.start, source, start),
|
||||
end: advancePositionWithClone(node.loc.start, source, end)
|
||||
})
|
||||
createSimpleExpression(
|
||||
id.name,
|
||||
false,
|
||||
{
|
||||
source,
|
||||
start: advancePositionWithClone(node.loc.start, source, start),
|
||||
end: advancePositionWithClone(node.loc.start, source, end)
|
||||
},
|
||||
id.isConstant /* isConstant */
|
||||
)
|
||||
)
|
||||
if (i === ids.length - 1 && end < rawExp.length) {
|
||||
children.push(rawExp.slice(end))
|
||||
@@ -206,6 +220,7 @@ export function processExpression(
|
||||
ret = createCompoundExpression(children, node.loc)
|
||||
} else {
|
||||
ret = node
|
||||
ret.isConstant = isConstant
|
||||
}
|
||||
ret.identifiers = Object.keys(knownIds)
|
||||
return ret
|
||||
|
||||
Reference in New Issue
Block a user