wip: fix render fn compat detection

This commit is contained in:
Evan You 2021-04-11 11:15:40 -04:00
parent a2f441dc0e
commit c55f3ed0e8
9 changed files with 86 additions and 20 deletions

View File

@ -30,6 +30,11 @@ export function isCompatEnabled(
key: DeprecationTypes,
instance: ComponentInternalInstance | null
): boolean {
// skip compat for built-in components
if (instance && instance.type.__isBuiltIn) {
return false
}
const mode = getCompatConfigForKey('MODE', instance) || 2
const val = getCompatConfigForKey(key, instance)
if (mode === 2) {

View File

@ -19,6 +19,10 @@ export function convertLegacyComponent(
comp: any,
instance: ComponentInternalInstance | null
): Component {
if (comp.__isBuiltIn) {
return comp
}
// 2.x async component
// since after disabling this, plain functions are still valid usage, do not
// use softAssert here.

View File

@ -49,7 +49,11 @@ export function installCompatInstanceProperties(map: PublicPropertiesMap) {
// overrides existing accessor
$slots: i => {
if (isCompatEnabled(DeprecationTypes.RENDER_FUNCTION, i)) {
if (
isCompatEnabled(DeprecationTypes.RENDER_FUNCTION, i) &&
i.render &&
i.render._compatWrapped
) {
return new Proxy(i.slots, legacySlotProxyHandlers)
}
return __DEV__ ? shallowReadonly(i.slots) : i.slots

View File

@ -5,7 +5,13 @@ import {
ShapeFlags,
toHandlerKey
} from '@vue/shared'
import { Component, Data } from '../component'
import {
Component,
ComponentInternalInstance,
ComponentOptions,
Data,
InternalRenderFunction
} from '../component'
import { DirectiveArguments, withDirectives } from '../directives'
import {
resolveDirective,
@ -19,6 +25,35 @@ import {
VNodeArrayChildren,
VNodeProps
} from '../vnode'
import { checkCompatEnabled } from './compatConfig'
import { DeprecationTypes } from './deprecations'
export function convertLegacyRenderFn(instance: ComponentInternalInstance) {
const Component = instance.type as ComponentOptions
const render = Component.render as InternalRenderFunction | undefined
// v3 runtime compiled, or already checked / wrapped
if (!render || render._rc || render._compatChecked || render._compatWrapped) {
return
}
const string = render.toString()
if (string.startsWith('function render(_ctx') || string.startsWith('(_ctx')) {
// v3 pre-compiled function
render._compatChecked = true
return
}
// v2 render function, try to provide compat
if (checkCompatEnabled(DeprecationTypes.RENDER_FUNCTION, instance)) {
const wrapped = (Component.render = function compatRender() {
// @ts-ignore
return render.call(this, compatH)
})
// @ts-ignore
wrapped._compatWrapped = true
}
}
interface LegacyVNodeProps {
key?: string | number

View File

@ -54,9 +54,7 @@ import { CompilerOptions } from '@vue/compiler-core'
import { markAttrsAccessed } from './componentRenderUtils'
import { currentRenderingInstance } from './componentRenderContext'
import { startMeasure, endMeasure } from './profiling'
import { checkCompatEnabled } from './compat/compatConfig'
import { DeprecationTypes } from './compat/deprecations'
import { compatH } from './compat/renderFn'
import { convertLegacyRenderFn } from './compat/renderFn'
export type Data = Record<string, unknown>
@ -96,6 +94,10 @@ export interface ComponentInternalOptions {
* @internal
*/
__hmrId?: string
/**
* Compat build only, for bailing out of certain compatibility behavior
*/
__isBuiltIn?: boolean
/**
* This one should be exposed so that devtools can make use of it
*/
@ -188,6 +190,10 @@ export type InternalRenderFunction = {
$options: ComponentInternalInstance['ctx']
): VNodeChild
_rc?: boolean // isRuntimeCompiled
// __COMPAT__ only
_compatChecked?: boolean // v3 and already checked for v2 compat
_compatWrapped?: boolean // is wrapped for v2 compat
}
/**
@ -684,15 +690,8 @@ export function finishComponentSetup(
) {
const Component = instance.type as ComponentOptions
if (
__COMPAT__ &&
Component.render &&
checkCompatEnabled(DeprecationTypes.RENDER_FUNCTION, instance)
) {
const originalRender = Component.render
Component.render = function compatRender() {
return originalRender.call(this, compatH)
}
if (__COMPAT__) {
convertLegacyRenderFn(instance)
}
// template / render function normalization

View File

@ -1,7 +1,8 @@
import {
getCurrentInstance,
SetupContext,
ComponentInternalInstance
ComponentInternalInstance,
ComponentOptions
} from '../component'
import {
cloneVNode,
@ -110,7 +111,7 @@ export function useTransitionState(): TransitionState {
const TransitionHookValidator = [Function, Array]
const BaseTransitionImpl = {
const BaseTransitionImpl: ComponentOptions = {
name: `BaseTransition`,
props: {
@ -250,6 +251,10 @@ const BaseTransitionImpl = {
}
}
if (__COMPAT__) {
BaseTransitionImpl.__isBuiltIn = true
}
// export the public type for h/tsx inference
// also to avoid inline import() in generated d.ts files
export const BaseTransition = (BaseTransitionImpl as any) as {

View File

@ -5,7 +5,8 @@ import {
ComponentInternalInstance,
LifecycleHooks,
currentInstance,
getComponentName
getComponentName,
ComponentOptions
} from '../component'
import { VNode, cloneVNode, isVNode, VNodeProps } from '../vnode'
import { warn } from '../warning'
@ -62,7 +63,7 @@ export interface KeepAliveContext extends ComponentRenderContext {
export const isKeepAlive = (vnode: VNode): boolean =>
(vnode.type as any).__isKeepAlive
const KeepAliveImpl = {
const KeepAliveImpl: ComponentOptions = {
name: `KeepAlive`,
// Marker for special handling inside the renderer. We are not using a ===
@ -298,6 +299,10 @@ const KeepAliveImpl = {
}
}
if (__COMPAT__) {
KeepAliveImpl.__isBuildIn = true
}
// export the public type for h/tsx inference
// also to avoid inline import() in generated d.ts files
export const KeepAlive = (KeepAliveImpl as any) as {

View File

@ -46,6 +46,10 @@ export const Transition: FunctionalComponent<TransitionProps> = (
Transition.displayName = 'Transition'
if (__COMPAT__) {
Transition.__isBuiltIn = true
}
const DOMTransitionPropsValidators = {
name: String,
type: String,

View File

@ -22,7 +22,8 @@ import {
SetupContext,
toRaw,
compatUtils,
DeprecationTypes
DeprecationTypes,
ComponentOptions
} from '@vue/runtime-core'
import { extend } from '@vue/shared'
@ -39,7 +40,7 @@ export type TransitionGroupProps = Omit<TransitionProps, 'mode'> & {
moveClass?: string
}
const TransitionGroupImpl = {
const TransitionGroupImpl: ComponentOptions = {
name: 'TransitionGroup',
props: /*#__PURE__*/ extend({}, TransitionPropsValidators, {
@ -145,6 +146,10 @@ const TransitionGroupImpl = {
}
}
if (__COMPAT__) {
TransitionGroupImpl.__isBuiltIn = true
}
/**
* TransitionGroup does not support "mode" so we need to remove it from the
* props declarations, but direct delete operation is considered a side effect