if (exp && !exp.content.trim()) {
exp = undefined
}
- let isCacheable: boolean = context.cacheHandlers && !exp
+ let shouldCache: boolean = context.cacheHandlers && !exp
if (exp) {
const isMemberExp = isMemberExpression(exp.content)
const isInlineStatement = !(isMemberExp || fnExpRE.test(exp.content))
isInlineStatement && context.removeIdentifiers(`$event`)
// with scope analysis, the function is hoistable if it has no reference
// to scope variables.
- isCacheable =
+ shouldCache =
context.cacheHandlers &&
+ // runtime constants don't need to be cached
+ // (this is analyzed by compileScript in SFC <script setup>)
+ !(exp.type === NodeTypes.SIMPLE_EXPRESSION && exp.isRuntimeConstant) &&
// #1541 bail if this is a member exp handler passed to a component -
// we need to use the original function to preserve arity,
// e.g. <transition> relies on checking cb.length to determine
// to a function, turn it into invocation (and wrap in an arrow function
// below) so that it always accesses the latest value when called - thus
// avoiding the need to be patched.
- if (isCacheable && isMemberExp) {
+ if (shouldCache && isMemberExp) {
if (exp.type === NodeTypes.SIMPLE_EXPRESSION) {
exp.content += `(...args)`
} else {
)
}
- if (isInlineStatement || (isCacheable && isMemberExp)) {
+ if (isInlineStatement || (shouldCache && isMemberExp)) {
// wrap inline statement in a function expression
exp = createCompoundExpression([
`${isInlineStatement ? `$event` : `(...args)`} => ${
ret = augmentor(ret)
}
- if (isCacheable) {
+ if (shouldCache) {
// cache handlers so that it's always the same handler being passed down.
// this avoids unnecessary re-renders when users use inline handlers on
// components.
import { RawSourceMap } from 'source-map'
import { genCssVarsCode, injectCssVarsCalls } from './genCssVars'
import { compileTemplate, SFCTemplateCompileOptions } from './compileTemplate'
+import { BindingTypes } from 'packages/compiler-core/src/options'
const CTX_FN_NAME = 'defineContext'
source: string
}
> = Object.create(null)
- const setupBindings: Record<string, 'var' | 'const'> = Object.create(null)
- const refBindings: Record<string, 'var'> = Object.create(null)
+ const setupBindings: Record<
+ string,
+ BindingTypes.SETUP | BindingTypes.CONST
+ > = Object.create(null)
+ const refBindings: Record<string, BindingTypes.SETUP> = Object.create(null)
const refIdentifiers: Set<Identifier> = new Set()
const enableRefSugar = options.refSugar !== false
let defaultExport: Node | undefined
if (id.name[0] === '$') {
error(`ref variable identifiers cannot start with $.`, id)
}
- refBindings[id.name] = setupBindings[id.name] = 'var'
+ refBindings[id.name] = setupBindings[id.name] = BindingTypes.SETUP
refIdentifiers.add(id)
}
}`
}
- const allBindings = { ...setupBindings }
+ const allBindings: Record<string, any> = { ...setupBindings }
for (const key in userImports) {
- allBindings[key] = 'var'
+ allBindings[key] = true
}
// 9. inject `useCssVars` calls
}
if (setupContextType) {
for (const key in typeDeclaredProps) {
- bindingMetadata[key] = 'props'
+ bindingMetadata[key] = BindingTypes.PROPS
}
}
if (setupContextArg) {
}
if (options.inlineTemplate) {
for (const [key, { source }] of Object.entries(userImports)) {
- bindingMetadata[key] = source.endsWith('.vue') ? 'setup-raw' : 'setup'
+ bindingMetadata[key] = source.endsWith('.vue')
+ ? BindingTypes.CONST
+ : BindingTypes.SETUP
}
for (const key in setupBindings) {
- bindingMetadata[key] =
- setupBindings[key] === 'var' ? 'setup' : 'setup-raw'
+ bindingMetadata[key] = setupBindings[key]
}
} else {
for (const key in allBindings) {
- bindingMetadata[key] = 'setup'
+ bindingMetadata[key] = BindingTypes.SETUP
}
}
}
}
-function walkDeclaration(node: Declaration, bindings: Record<string, string>) {
+function walkDeclaration(
+ node: Declaration,
+ bindings: Record<string, BindingTypes>
+) {
if (node.type === 'VariableDeclaration') {
const isConst = node.kind === 'const'
// export const foo = ...
for (const { id, init } of node.declarations) {
+ const isContextCall = !!(
+ isConst &&
+ init &&
+ init.type === 'CallExpression' &&
+ init.callee.type === 'Identifier' &&
+ init.callee.name === CTX_FN_NAME
+ )
if (id.type === 'Identifier') {
bindings[id.name] =
// if a declaration is a const literal, we can mark it so that
// the generated render fn code doesn't need to unref() it
- isConst &&
+ isContextCall ||
+ (isConst &&
init!.type !== 'Identifier' && // const a = b
init!.type !== 'CallExpression' && // const a = ref()
- init!.type !== 'MemberExpression' // const a = b.c
- ? 'const'
- : 'var'
+ init!.type !== 'MemberExpression') // const a = b.c
+ ? BindingTypes.CONST
+ : BindingTypes.SETUP
} else if (id.type === 'ObjectPattern') {
- walkObjectPattern(id, bindings, isConst)
+ walkObjectPattern(id, bindings, isConst, isContextCall)
} else if (id.type === 'ArrayPattern') {
- walkArrayPattern(id, bindings, isConst)
+ walkArrayPattern(id, bindings, isConst, isContextCall)
}
}
} else if (
) {
// export function foo() {} / export class Foo {}
// export declarations must be named.
- bindings[node.id!.name] = 'const'
+ bindings[node.id!.name] = BindingTypes.CONST
}
}
function walkObjectPattern(
node: ObjectPattern,
- bindings: Record<string, string>,
- isConst: boolean
+ bindings: Record<string, BindingTypes>,
+ isConst: boolean,
+ isContextCall = false
) {
for (const p of node.properties) {
if (p.type === 'ObjectProperty') {
if (p.key.type === 'Identifier') {
if (p.key === p.value) {
// const { x } = ...
- bindings[p.key.name] = 'var'
+ bindings[p.key.name] = isContextCall
+ ? BindingTypes.CONST
+ : BindingTypes.SETUP
} else {
- walkPattern(p.value, bindings, isConst)
+ walkPattern(p.value, bindings, isConst, isContextCall)
}
}
} else {
// ...rest
// argument can only be identifer when destructuring
- bindings[(p.argument as Identifier).name] = isConst ? 'const' : 'var'
+ bindings[(p.argument as Identifier).name] = isConst
+ ? BindingTypes.CONST
+ : BindingTypes.SETUP
}
}
}
function walkArrayPattern(
node: ArrayPattern,
- bindings: Record<string, string>,
- isConst: boolean
+ bindings: Record<string, BindingTypes>,
+ isConst: boolean,
+ isContextCall = false
) {
for (const e of node.elements) {
- e && walkPattern(e, bindings, isConst)
+ e && walkPattern(e, bindings, isConst, isContextCall)
}
}
function walkPattern(
node: Node,
- bindings: Record<string, string>,
- isConst: boolean
+ bindings: Record<string, BindingTypes>,
+ isConst: boolean,
+ isContextCall = false
) {
if (node.type === 'Identifier') {
- bindings[node.name] = 'var'
+ bindings[node.name] = isContextCall
+ ? BindingTypes.CONST
+ : BindingTypes.SETUP
} else if (node.type === 'RestElement') {
// argument can only be identifer when destructuring
- bindings[(node.argument as Identifier).name] = isConst ? 'const' : 'var'
+ bindings[(node.argument as Identifier).name] = isConst
+ ? BindingTypes.CONST
+ : BindingTypes.SETUP
} else if (node.type === 'ObjectPattern') {
walkObjectPattern(node, bindings, isConst)
} else if (node.type === 'ArrayPattern') {
walkArrayPattern(node, bindings, isConst)
} else if (node.type === 'AssignmentPattern') {
if (node.left.type === 'Identifier') {
- bindings[node.left.name] = 'var'
+ bindings[node.left.name] = isContextCall
+ ? BindingTypes.CONST
+ : BindingTypes.SETUP
} else {
walkPattern(node.left, bindings, isConst)
}
// props: ['foo']
// props: { foo: ... }
for (const key of getObjectOrArrayExpressionKeys(property)) {
- bindings[key] = 'props'
+ bindings[key] = BindingTypes.PROPS
}
}
// inject: ['foo']
// inject: { foo: {} }
for (const key of getObjectOrArrayExpressionKeys(property)) {
- bindings[key] = 'options'
+ bindings[key] = BindingTypes.OPTIONS
}
}
// methods: { foo() {} }
// computed: { foo() {} }
for (const key of getObjectExpressionKeys(property.value)) {
- bindings[key] = 'options'
+ bindings[key] = BindingTypes.OPTIONS
}
}
}
bodyItem.argument.type === 'ObjectExpression'
) {
for (const key of getObjectExpressionKeys(bodyItem.argument)) {
- bindings[key] = property.key.name
+ bindings[key] =
+ property.key.name === 'setup'
+ ? BindingTypes.SETUP
+ : BindingTypes.DATA
}
}
}