feat(transition): properly handle transition & transition-group in compiler
This commit is contained in:
parent
4e8d57bdfb
commit
0e3e07079a
packages
compiler-core/src
compiler-dom/src
runtime-dom/src/components
@ -1,4 +1,4 @@
|
|||||||
import { NO, makeMap, isArray } from '@vue/shared'
|
import { NO, isArray } from '@vue/shared'
|
||||||
import {
|
import {
|
||||||
ErrorCodes,
|
ErrorCodes,
|
||||||
createCompilerError,
|
createCompilerError,
|
||||||
@ -8,7 +8,8 @@ import {
|
|||||||
import {
|
import {
|
||||||
assert,
|
assert,
|
||||||
advancePositionWithMutation,
|
advancePositionWithMutation,
|
||||||
advancePositionWithClone
|
advancePositionWithClone,
|
||||||
|
isCoreComponent
|
||||||
} from './utils'
|
} from './utils'
|
||||||
import {
|
import {
|
||||||
Namespace,
|
Namespace,
|
||||||
@ -29,22 +30,12 @@ import {
|
|||||||
} from './ast'
|
} from './ast'
|
||||||
import { extend } from '@vue/shared'
|
import { extend } from '@vue/shared'
|
||||||
|
|
||||||
// Portal and Fragment are native types, not components
|
|
||||||
const isBuiltInComponent = /*#__PURE__*/ makeMap(
|
|
||||||
`suspense,keep-alive,base-transition`,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
|
|
||||||
export interface ParserOptions {
|
export interface ParserOptions {
|
||||||
isVoidTag?: (tag: string) => boolean // e.g. img, br, hr
|
isVoidTag?: (tag: string) => boolean // e.g. img, br, hr
|
||||||
isNativeTag?: (tag: string) => boolean // e.g. loading-indicator in weex
|
isNativeTag?: (tag: string) => boolean // e.g. loading-indicator in weex
|
||||||
isPreTag?: (tag: string) => boolean // e.g. <pre> where whitespace is intact
|
isPreTag?: (tag: string) => boolean // e.g. <pre> where whitespace is intact
|
||||||
isCustomElement?: (tag: string) => boolean
|
isCustomElement?: (tag: string) => boolean
|
||||||
// for importing platform-specific components from the runtime.
|
isBuiltInComponent?: (tag: string) => symbol | void
|
||||||
// e.g. <transition> for runtime-dom
|
|
||||||
// However this is only needed if isNativeTag is not specified, since when
|
|
||||||
// isNativeTag is specified anything that is not native is a component.
|
|
||||||
isBuiltInComponent?: (tag: string) => boolean
|
|
||||||
getNamespace?: (tag: string, parent: ElementNode | undefined) => Namespace
|
getNamespace?: (tag: string, parent: ElementNode | undefined) => Namespace
|
||||||
getTextMode?: (tag: string, ns: Namespace) => TextModes
|
getTextMode?: (tag: string, ns: Namespace) => TextModes
|
||||||
delimiters?: [string, string] // ['{{', '}}']
|
delimiters?: [string, string] // ['{{', '}}']
|
||||||
@ -483,7 +474,7 @@ function parseTag(
|
|||||||
if (options.isNativeTag) {
|
if (options.isNativeTag) {
|
||||||
if (!options.isNativeTag(tag)) tagType = ElementTypes.COMPONENT
|
if (!options.isNativeTag(tag)) tagType = ElementTypes.COMPONENT
|
||||||
} else if (
|
} else if (
|
||||||
isBuiltInComponent(tag) ||
|
isCoreComponent(tag) ||
|
||||||
(options.isBuiltInComponent && options.isBuiltInComponent(tag)) ||
|
(options.isBuiltInComponent && options.isBuiltInComponent(tag)) ||
|
||||||
/^[A-Z]/.test(tag)
|
/^[A-Z]/.test(tag)
|
||||||
) {
|
) {
|
||||||
|
@ -17,7 +17,7 @@ import {
|
|||||||
CacheExpression,
|
CacheExpression,
|
||||||
createCacheExpression
|
createCacheExpression
|
||||||
} from './ast'
|
} from './ast'
|
||||||
import { isString, isArray } from '@vue/shared'
|
import { isString, isArray, NOOP } from '@vue/shared'
|
||||||
import { CompilerError, defaultOnError } from './errors'
|
import { CompilerError, defaultOnError } from './errors'
|
||||||
import {
|
import {
|
||||||
TO_STRING,
|
TO_STRING,
|
||||||
@ -68,6 +68,7 @@ export type StructuralDirectiveTransform = (
|
|||||||
export interface TransformOptions {
|
export interface TransformOptions {
|
||||||
nodeTransforms?: NodeTransform[]
|
nodeTransforms?: NodeTransform[]
|
||||||
directiveTransforms?: { [name: string]: DirectiveTransform }
|
directiveTransforms?: { [name: string]: DirectiveTransform }
|
||||||
|
isBuiltInComponent?: (tag: string) => symbol | void
|
||||||
prefixIdentifiers?: boolean
|
prefixIdentifiers?: boolean
|
||||||
hoistStatic?: boolean
|
hoistStatic?: boolean
|
||||||
cacheHandlers?: boolean
|
cacheHandlers?: boolean
|
||||||
@ -110,6 +111,7 @@ function createTransformContext(
|
|||||||
cacheHandlers = false,
|
cacheHandlers = false,
|
||||||
nodeTransforms = [],
|
nodeTransforms = [],
|
||||||
directiveTransforms = {},
|
directiveTransforms = {},
|
||||||
|
isBuiltInComponent = NOOP,
|
||||||
onError = defaultOnError
|
onError = defaultOnError
|
||||||
}: TransformOptions
|
}: TransformOptions
|
||||||
): TransformContext {
|
): TransformContext {
|
||||||
@ -132,6 +134,7 @@ function createTransformContext(
|
|||||||
cacheHandlers,
|
cacheHandlers,
|
||||||
nodeTransforms,
|
nodeTransforms,
|
||||||
directiveTransforms,
|
directiveTransforms,
|
||||||
|
isBuiltInComponent,
|
||||||
onError,
|
onError,
|
||||||
parent: null,
|
parent: null,
|
||||||
currentNode: root,
|
currentNode: root,
|
||||||
|
@ -15,7 +15,7 @@ import {
|
|||||||
createObjectExpression,
|
createObjectExpression,
|
||||||
Property
|
Property
|
||||||
} from '../ast'
|
} from '../ast'
|
||||||
import { PatchFlags, PatchFlagNames, isSymbol, hyphenate } from '@vue/shared'
|
import { PatchFlags, PatchFlagNames, isSymbol } from '@vue/shared'
|
||||||
import { createCompilerError, ErrorCodes } from '../errors'
|
import { createCompilerError, ErrorCodes } from '../errors'
|
||||||
import {
|
import {
|
||||||
CREATE_VNODE,
|
CREATE_VNODE,
|
||||||
@ -26,11 +26,15 @@ import {
|
|||||||
MERGE_PROPS,
|
MERGE_PROPS,
|
||||||
TO_HANDLERS,
|
TO_HANDLERS,
|
||||||
PORTAL,
|
PORTAL,
|
||||||
SUSPENSE,
|
KEEP_ALIVE
|
||||||
KEEP_ALIVE,
|
|
||||||
BASE_TRANSITION
|
|
||||||
} from '../runtimeHelpers'
|
} from '../runtimeHelpers'
|
||||||
import { getInnerRange, isVSlot, toValidAssetId, findProp } from '../utils'
|
import {
|
||||||
|
getInnerRange,
|
||||||
|
isVSlot,
|
||||||
|
toValidAssetId,
|
||||||
|
findProp,
|
||||||
|
isCoreComponent
|
||||||
|
} from '../utils'
|
||||||
import { buildSlots } from './vSlot'
|
import { buildSlots } from './vSlot'
|
||||||
import { isStaticNode } from './hoistStatic'
|
import { isStaticNode } from './hoistStatic'
|
||||||
|
|
||||||
@ -38,9 +42,6 @@ import { isStaticNode } from './hoistStatic'
|
|||||||
// import, which should be used instead of a resolveDirective call.
|
// import, which should be used instead of a resolveDirective call.
|
||||||
const directiveImportMap = new WeakMap<DirectiveNode, symbol>()
|
const directiveImportMap = new WeakMap<DirectiveNode, symbol>()
|
||||||
|
|
||||||
const isBuiltInType = (tag: string, expected: string): boolean =>
|
|
||||||
tag === expected || tag === hyphenate(expected)
|
|
||||||
|
|
||||||
// generate a JavaScript AST for this element's codegen
|
// generate a JavaScript AST for this element's codegen
|
||||||
export const transformElement: NodeTransform = (node, context) => {
|
export const transformElement: NodeTransform = (node, context) => {
|
||||||
if (
|
if (
|
||||||
@ -57,10 +58,8 @@ export const transformElement: NodeTransform = (node, context) => {
|
|||||||
// processed and merged.
|
// processed and merged.
|
||||||
return function postTransformElement() {
|
return function postTransformElement() {
|
||||||
const { tag, tagType, props } = node
|
const { tag, tagType, props } = node
|
||||||
const isPortal = isBuiltInType(tag, 'Portal')
|
const builtInComponentSymbol =
|
||||||
const isSuspense = isBuiltInType(tag, 'Suspense')
|
isCoreComponent(tag) || context.isBuiltInComponent(tag)
|
||||||
const isKeepAlive = isBuiltInType(tag, 'KeepAlive')
|
|
||||||
const isBaseTransition = isBuiltInType(tag, 'BaseTransition')
|
|
||||||
const isComponent = tagType === ElementTypes.COMPONENT
|
const isComponent = tagType === ElementTypes.COMPONENT
|
||||||
|
|
||||||
let hasProps = props.length > 0
|
let hasProps = props.length > 0
|
||||||
@ -96,14 +95,8 @@ export const transformElement: NodeTransform = (node, context) => {
|
|||||||
let nodeType
|
let nodeType
|
||||||
if (dynamicComponent) {
|
if (dynamicComponent) {
|
||||||
nodeType = dynamicComponent
|
nodeType = dynamicComponent
|
||||||
} else if (isPortal) {
|
} else if (builtInComponentSymbol) {
|
||||||
nodeType = context.helper(PORTAL)
|
nodeType = context.helper(builtInComponentSymbol)
|
||||||
} else if (isSuspense) {
|
|
||||||
nodeType = context.helper(SUSPENSE)
|
|
||||||
} else if (isKeepAlive) {
|
|
||||||
nodeType = context.helper(KEEP_ALIVE)
|
|
||||||
} else if (isBaseTransition) {
|
|
||||||
nodeType = context.helper(BASE_TRANSITION)
|
|
||||||
} else if (isComponent) {
|
} else if (isComponent) {
|
||||||
// user component w/ resolve
|
// user component w/ resolve
|
||||||
context.helper(RESOLVE_COMPONENT)
|
context.helper(RESOLVE_COMPONENT)
|
||||||
@ -142,7 +135,11 @@ export const transformElement: NodeTransform = (node, context) => {
|
|||||||
// Portal is not a real component has dedicated handling in the renderer
|
// Portal is not a real component has dedicated handling in the renderer
|
||||||
// KeepAlive should not track its own deps so that it can be used inside
|
// KeepAlive should not track its own deps so that it can be used inside
|
||||||
// Transition
|
// Transition
|
||||||
if (isComponent && !isPortal && !isKeepAlive) {
|
if (
|
||||||
|
isComponent &&
|
||||||
|
builtInComponentSymbol !== PORTAL &&
|
||||||
|
builtInComponentSymbol !== KEEP_ALIVE
|
||||||
|
) {
|
||||||
const { slots, hasDynamicSlots } = buildSlots(node, context)
|
const { slots, hasDynamicSlots } = buildSlots(node, context)
|
||||||
args.push(slots)
|
args.push(slots)
|
||||||
if (hasDynamicSlots) {
|
if (hasDynamicSlots) {
|
||||||
|
@ -27,8 +27,31 @@ import {
|
|||||||
import { parse } from 'acorn'
|
import { parse } from 'acorn'
|
||||||
import { walk } from 'estree-walker'
|
import { walk } from 'estree-walker'
|
||||||
import { TransformContext } from './transform'
|
import { TransformContext } from './transform'
|
||||||
import { OPEN_BLOCK, MERGE_PROPS, RENDER_SLOT } from './runtimeHelpers'
|
import {
|
||||||
import { isString, isFunction, isObject } from '@vue/shared'
|
OPEN_BLOCK,
|
||||||
|
MERGE_PROPS,
|
||||||
|
RENDER_SLOT,
|
||||||
|
PORTAL,
|
||||||
|
SUSPENSE,
|
||||||
|
KEEP_ALIVE,
|
||||||
|
BASE_TRANSITION
|
||||||
|
} from './runtimeHelpers'
|
||||||
|
import { isString, isFunction, isObject, hyphenate } from '@vue/shared'
|
||||||
|
|
||||||
|
export const isBuiltInType = (tag: string, expected: string): boolean =>
|
||||||
|
tag === expected || tag === hyphenate(expected)
|
||||||
|
|
||||||
|
export function isCoreComponent(tag: string): symbol | void {
|
||||||
|
if (isBuiltInType(tag, 'Portal')) {
|
||||||
|
return PORTAL
|
||||||
|
} else if (isBuiltInType(tag, 'Suspense')) {
|
||||||
|
return SUSPENSE
|
||||||
|
} else if (isBuiltInType(tag, 'KeepAlive')) {
|
||||||
|
return KEEP_ALIVE
|
||||||
|
} else if (isBuiltInType(tag, 'BaseTransition')) {
|
||||||
|
return BASE_TRANSITION
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// cache node requires
|
// cache node requires
|
||||||
// lazy require dependencies so that they don't end up in rollup's dep graph
|
// lazy require dependencies so that they don't end up in rollup's dep graph
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
import { baseCompile, CompilerOptions, CodegenResult } from '@vue/compiler-core'
|
import {
|
||||||
|
baseCompile,
|
||||||
|
CompilerOptions,
|
||||||
|
CodegenResult,
|
||||||
|
isBuiltInType
|
||||||
|
} from '@vue/compiler-core'
|
||||||
import { parserOptionsMinimal } from './parserOptionsMinimal'
|
import { parserOptionsMinimal } from './parserOptionsMinimal'
|
||||||
import { parserOptionsStandard } from './parserOptionsStandard'
|
import { parserOptionsStandard } from './parserOptionsStandard'
|
||||||
import { transformStyle } from './transforms/transformStyle'
|
import { transformStyle } from './transforms/transformStyle'
|
||||||
@ -8,6 +13,7 @@ import { transformVText } from './transforms/vText'
|
|||||||
import { transformModel } from './transforms/vModel'
|
import { transformModel } from './transforms/vModel'
|
||||||
import { transformOn } from './transforms/vOn'
|
import { transformOn } from './transforms/vOn'
|
||||||
import { transformShow } from './transforms/vShow'
|
import { transformShow } from './transforms/vShow'
|
||||||
|
import { TRANSITION, TRANSITION_GROUP } from './runtimeHelpers'
|
||||||
|
|
||||||
export function compile(
|
export function compile(
|
||||||
template: string,
|
template: string,
|
||||||
@ -25,6 +31,13 @@ export function compile(
|
|||||||
on: transformOn,
|
on: transformOn,
|
||||||
show: transformShow,
|
show: transformShow,
|
||||||
...(options.directiveTransforms || {})
|
...(options.directiveTransforms || {})
|
||||||
|
},
|
||||||
|
isBuiltInComponent: tag => {
|
||||||
|
if (isBuiltInType(tag, `Transition`)) {
|
||||||
|
return TRANSITION
|
||||||
|
} else if (isBuiltInType(tag, `TransitionGroup`)) {
|
||||||
|
return TRANSITION_GROUP
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,9 @@ export const V_ON_WITH_KEYS = Symbol(__DEV__ ? `vOnKeysGuard` : ``)
|
|||||||
|
|
||||||
export const V_SHOW = Symbol(__DEV__ ? `vShow` : ``)
|
export const V_SHOW = Symbol(__DEV__ ? `vShow` : ``)
|
||||||
|
|
||||||
|
export const TRANSITION = Symbol(__DEV__ ? `Transition` : ``)
|
||||||
|
export const TRANSITION_GROUP = Symbol(__DEV__ ? `TransitionGroup` : ``)
|
||||||
|
|
||||||
registerRuntimeHelpers({
|
registerRuntimeHelpers({
|
||||||
[V_MODEL_RADIO]: `vModelRadio`,
|
[V_MODEL_RADIO]: `vModelRadio`,
|
||||||
[V_MODEL_CHECKBOX]: `vModelCheckbox`,
|
[V_MODEL_CHECKBOX]: `vModelCheckbox`,
|
||||||
@ -19,5 +22,7 @@ registerRuntimeHelpers({
|
|||||||
[V_MODEL_DYNAMIC]: `vModelDynamic`,
|
[V_MODEL_DYNAMIC]: `vModelDynamic`,
|
||||||
[V_ON_WITH_MODIFIERS]: `withModifiers`,
|
[V_ON_WITH_MODIFIERS]: `withModifiers`,
|
||||||
[V_ON_WITH_KEYS]: `withKeys`,
|
[V_ON_WITH_KEYS]: `withKeys`,
|
||||||
[V_SHOW]: `vShow`
|
[V_SHOW]: `vShow`,
|
||||||
|
[TRANSITION]: `Transition`,
|
||||||
|
[TRANSITION_GROUP]: `TransitionGroup`
|
||||||
})
|
})
|
||||||
|
@ -95,6 +95,11 @@ export const TransitionGroup = {
|
|||||||
prevChildren = children
|
prevChildren = children
|
||||||
children = slots.default ? slots.default() : []
|
children = slots.default ? slots.default() : []
|
||||||
|
|
||||||
|
// handle fragment children case, e.g. v-for
|
||||||
|
if (children.length === 1 && children[0].type === Fragment) {
|
||||||
|
children = children[0].children as VNode[]
|
||||||
|
}
|
||||||
|
|
||||||
for (let i = 0; i < children.length; i++) {
|
for (let i = 0; i < children.length; i++) {
|
||||||
const child = children[i]
|
const child = children[i]
|
||||||
if (child.key != null) {
|
if (child.key != null) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user