|
|
@@ -19,13 +19,21 @@ import {
|
|
|
FunctionExpression,
|
|
|
CallExpression,
|
|
|
createCallExpression,
|
|
|
- createArrayExpression
|
|
|
+ createArrayExpression,
|
|
|
+ IfBranchNode
|
|
|
} from '../ast'
|
|
|
import { TransformContext, NodeTransform } from '../transform'
|
|
|
import { createCompilerError, ErrorCodes } from '../errors'
|
|
|
-import { findDir, isTemplateNode, assert, isVSlot } from '../utils'
|
|
|
+import {
|
|
|
+ findDir,
|
|
|
+ isTemplateNode,
|
|
|
+ assert,
|
|
|
+ isVSlot,
|
|
|
+ isSimpleIdentifier
|
|
|
+} from '../utils'
|
|
|
import { CREATE_SLOTS, RENDER_LIST } from '../runtimeHelpers'
|
|
|
import { parseForExpression, createForLoopParams } from './vFor'
|
|
|
+import { isObject } from '@vue/shared'
|
|
|
|
|
|
const isStaticExp = (p: JSChildNode): p is SimpleExpressionNode =>
|
|
|
p.type === NodeTypes.SIMPLE_EXPRESSION && p.isStatic
|
|
|
@@ -108,9 +116,12 @@ export function buildSlots(
|
|
|
|
|
|
// If the slot is inside a v-for or another v-slot, force it to be dynamic
|
|
|
// since it likely uses a scope variable.
|
|
|
- // TODO: This can be further optimized to only make it dynamic when the slot
|
|
|
- // actually uses the scope variables.
|
|
|
- let hasDynamicSlots = context.scopes.vSlot > 1 || context.scopes.vFor > 0
|
|
|
+ let hasDynamicSlots = context.scopes.vSlot > 0 || context.scopes.vFor > 0
|
|
|
+ // with `prefixIdentifiers: true`, this can be further optimized to make
|
|
|
+ // it dynamic only when the slot actually uses the scope variables.
|
|
|
+ if (!__BROWSER__ && context.prefixIdentifiers) {
|
|
|
+ hasDynamicSlots = hasScopeRef(node, context.identifiers)
|
|
|
+ }
|
|
|
|
|
|
// 1. Check for default slot with slotProps on component itself.
|
|
|
// <Comp v-slot="{ prop }"/>
|
|
|
@@ -326,3 +337,49 @@ function buildDynamicSlot(
|
|
|
createObjectProperty(`fn`, fn)
|
|
|
])
|
|
|
}
|
|
|
+
|
|
|
+function hasScopeRef(
|
|
|
+ node: TemplateChildNode | IfBranchNode | SimpleExpressionNode | undefined,
|
|
|
+ ids: TransformContext['identifiers']
|
|
|
+): boolean {
|
|
|
+ if (!node) {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ switch (node.type) {
|
|
|
+ case NodeTypes.ELEMENT:
|
|
|
+ for (let i = 0; i < node.props.length; i++) {
|
|
|
+ const p = node.props[i]
|
|
|
+ if (
|
|
|
+ p.type === NodeTypes.DIRECTIVE &&
|
|
|
+ (hasScopeRef(p.arg, ids) || hasScopeRef(p.exp, ids))
|
|
|
+ ) {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return node.children.some(c => hasScopeRef(c, ids))
|
|
|
+ case NodeTypes.FOR:
|
|
|
+ if (hasScopeRef(node.source, ids)) {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ return node.children.some(c => hasScopeRef(c, ids))
|
|
|
+ case NodeTypes.IF:
|
|
|
+ return node.branches.some(b => hasScopeRef(b, ids))
|
|
|
+ case NodeTypes.IF_BRANCH:
|
|
|
+ if (hasScopeRef(node.condition, ids)) {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ return node.children.some(c => hasScopeRef(c, ids))
|
|
|
+ case NodeTypes.SIMPLE_EXPRESSION:
|
|
|
+ return (
|
|
|
+ !node.isStatic &&
|
|
|
+ isSimpleIdentifier(node.content) &&
|
|
|
+ !!ids[node.content]
|
|
|
+ )
|
|
|
+ case NodeTypes.COMPOUND_EXPRESSION:
|
|
|
+ return node.children.some(c => isObject(c) && hasScopeRef(c, ids))
|
|
|
+ case NodeTypes.INTERPOLATION:
|
|
|
+ return hasScopeRef(node.content, ids)
|
|
|
+ default:
|
|
|
+ return false
|
|
|
+ }
|
|
|
+}
|