diff --git a/packages/compiler-core/__tests__/transforms/__snapshots__/hoistStatic.spec.ts.snap b/packages/compiler-core/__tests__/transforms/__snapshots__/hoistStatic.spec.ts.snap
index dba4bf1f0..9ad4a155c 100644
--- a/packages/compiler-core/__tests__/transforms/__snapshots__/hoistStatic.spec.ts.snap
+++ b/packages/compiler-core/__tests__/transforms/__snapshots__/hoistStatic.spec.ts.snap
@@ -173,7 +173,7 @@ exports[`compiler: hoistStatic transform prefixIdentifiers hoist nested static t
"const _Vue = Vue
const _createVNode = Vue.createVNode
-const _hoisted_1 = _createVNode(\\"span\\", null, [\\"foo \\", _toString(1), _toString(2)])
+const _hoisted_1 = _createVNode(\\"span\\", null, [\\"foo \\", _toString(1), _toString(true)])
return function render() {
with (this) {
@@ -203,7 +203,7 @@ return function render() {
}"
`;
-exports[`compiler: hoistStatic transform prefixIdentifiers should NOT hoist expressions that with scope variable (2) 1`] = `
+exports[`compiler: hoistStatic transform prefixIdentifiers should NOT hoist expressions that refer scope variables (2) 1`] = `
"const _Vue = Vue
return function render() {
@@ -221,7 +221,24 @@ return function render() {
}"
`;
-exports[`compiler: hoistStatic transform prefixIdentifiers should NOT hoist expressions that with scope variable 1`] = `
+exports[`compiler: hoistStatic transform prefixIdentifiers should NOT hoist expressions that refer scope variables (v-slot) 1`] = `
+"const _Vue = Vue
+
+return function render() {
+ with (this) {
+ const { toString: _toString, resolveComponent: _resolveComponent, createVNode: _createVNode, createBlock: _createBlock, openBlock: _openBlock } = _Vue
+
+ const _component_Comp = _resolveComponent(\\"Comp\\")
+
+ return (_openBlock(), _createBlock(_component_Comp, null, {
+ default: ({ foo }) => [_toString(_ctx.foo)],
+ _compiled: true
+ }))
+ }
+}"
+`;
+
+exports[`compiler: hoistStatic transform prefixIdentifiers should NOT hoist expressions that refer scope variables 1`] = `
"const _Vue = Vue
return function render() {
diff --git a/packages/compiler-core/__tests__/transforms/hoistStatic.spec.ts b/packages/compiler-core/__tests__/transforms/hoistStatic.spec.ts
index d998aa26c..bf4e2370b 100644
--- a/packages/compiler-core/__tests__/transforms/hoistStatic.spec.ts
+++ b/packages/compiler-core/__tests__/transforms/hoistStatic.spec.ts
@@ -446,7 +446,7 @@ describe('compiler: hoistStatic transform', () => {
describe('prefixIdentifiers', () => {
test('hoist nested static tree with static interpolation', () => {
const { root, args } = transformWithHoist(
- `
foo {{ 1 }} {{ 2 }}
`,
+ `foo {{ 1 }} {{ true }}
`,
{
prefixIdentifiers: true
}
@@ -474,7 +474,7 @@ describe('compiler: hoistStatic transform', () => {
{
type: NodeTypes.INTERPOLATION,
content: {
- content: `2`,
+ content: `true`,
isStatic: false,
isConstant: true
}
@@ -600,7 +600,7 @@ describe('compiler: hoistStatic transform', () => {
expect(generate(root).code).toMatchSnapshot()
})
- test('should NOT hoist expressions that with scope variable', () => {
+ test('should NOT hoist expressions that refer scope variables', () => {
const { root } = transformWithHoist(
``,
{
@@ -612,7 +612,7 @@ describe('compiler: hoistStatic transform', () => {
expect(generate(root).code).toMatchSnapshot()
})
- test('should NOT hoist expressions that with scope variable (2)', () => {
+ test('should NOT hoist expressions that refer scope variables (2)', () => {
const { root } = transformWithHoist(
``,
{
@@ -623,5 +623,17 @@ describe('compiler: hoistStatic transform', () => {
expect(root.hoists.length).toBe(0)
expect(generate(root).code).toMatchSnapshot()
})
+
+ test('should NOT hoist expressions that refer scope variables (v-slot)', () => {
+ const { root } = transformWithHoist(
+ `{{ foo }}`,
+ {
+ prefixIdentifiers: true
+ }
+ )
+
+ expect(root.hoists.length).toBe(0)
+ expect(generate(root).code).toMatchSnapshot()
+ })
})
})
diff --git a/packages/compiler-core/src/parse.ts b/packages/compiler-core/src/parse.ts
index c369d4564..f2520d179 100644
--- a/packages/compiler-core/src/parse.ts
+++ b/packages/compiler-core/src/parse.ts
@@ -564,12 +564,9 @@ 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(
@@ -585,7 +582,7 @@ function parseAttribute(
type: NodeTypes.SIMPLE_EXPRESSION,
content,
isStatic,
- isConstant,
+ isConstant: isStatic,
loc
}
}
@@ -611,7 +608,8 @@ function parseAttribute(
type: NodeTypes.SIMPLE_EXPRESSION,
content: value.content,
isStatic: false,
- // Set `isConstant` to false by default and will decide in transformExpression
+ // Treat as non-constant by default. This can be potentially set to
+ // true by `transformExpression` to make it eligible for hoisting.
isConstant: false,
loc: value.loc
},
diff --git a/packages/compiler-core/src/transforms/transformExpression.ts b/packages/compiler-core/src/transforms/transformExpression.ts
index 083b3023c..2bac56a84 100644
--- a/packages/compiler-core/src/transforms/transformExpression.ts
+++ b/packages/compiler-core/src/transforms/transformExpression.ts
@@ -91,6 +91,9 @@ export function processExpression(
!literalsWhitelist.has(rawExp)
) {
node.content = `_ctx.${rawExp}`
+ } else if (!context.identifiers[rawExp]) {
+ // mark node constant for hoisting unless it's referring a scope variable
+ node.isConstant = true
}
return node
}
@@ -109,13 +112,13 @@ 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) {
if (node.type === 'Identifier') {
if (!ids.includes(node)) {
- if (!knownIds[node.name] && shouldPrefix(node, parent)) {
+ const needPrefix = shouldPrefix(node, parent)
+ if (!knownIds[node.name] && needPrefix) {
if (isPropertyShorthand(node, parent)) {
// property shorthand like { foo }, we need to add the key since we
// rewrite the value
@@ -123,14 +126,11 @@ export function processExpression(
}
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)
- )
+ // The identifier is considered constant unless it's pointing to a
+ // scope variable (a v-for alias, or a v-slot prop)
+ node.isConstant = !(needPrefix && knownIds[node.name])
// also generate sub-expressions for other identifiers for better
// source map support. (except for property keys which are static)
ids.push(node)
@@ -220,7 +220,7 @@ export function processExpression(
ret = createCompoundExpression(children, node.loc)
} else {
ret = node
- ret.isConstant = isConstant
+ ret.isConstant = true
}
ret.identifiers = Object.keys(knownIds)
return ret