wip: more compat progress
This commit is contained in:
@@ -262,7 +262,8 @@ const deprecationData: Record<DeprecationTypes, DeprecationData> = {
|
||||
|
||||
[DeprecationTypes.PROPS_DEFAULT_THIS]: {
|
||||
message: (key: string) =>
|
||||
`props default value function no longer has access to "this". ` +
|
||||
`props default value function no longer has access to "this". The compat ` +
|
||||
`build only offers access to this.$options.` +
|
||||
`(found in prop "${key}")`,
|
||||
link: `https://v3.vuejs.org/guide/migration/props-default-this.html`
|
||||
},
|
||||
|
||||
@@ -132,7 +132,7 @@ function convertLegacyFunctionalComponent(comp: ComponentOptions) {
|
||||
data: instance.vnode.props || {},
|
||||
scopedSlots: ctx.slots,
|
||||
parent: instance.parent && instance.parent.proxy,
|
||||
get slots() {
|
||||
slots() {
|
||||
return new Proxy(ctx.slots, legacySlotProxyHandlers)
|
||||
},
|
||||
get listeners() {
|
||||
|
||||
@@ -12,13 +12,16 @@ import { getCompatListeners } from './instanceListeners'
|
||||
import { shallowReadonly } from '@vue/reactivity'
|
||||
import { legacySlotProxyHandlers } from './component'
|
||||
import { compatH } from './renderFn'
|
||||
import {
|
||||
legacyBindObjectProps,
|
||||
legacyRenderSlot,
|
||||
legacyRenderStatic
|
||||
} from './renderHelpers'
|
||||
import { createCommentVNode, createTextVNode } from '../vnode'
|
||||
import { renderList } from '../helpers/renderList'
|
||||
import {
|
||||
legacyBindObjectListeners,
|
||||
legacyBindObjectProps,
|
||||
legacyCheckKeyCodes,
|
||||
legacyRenderSlot,
|
||||
legacyRenderStatic,
|
||||
legacyresolveScopedSlots
|
||||
} from './renderHelpers'
|
||||
|
||||
export function installCompatInstanceProperties(map: PublicPropertiesMap) {
|
||||
const set = (target: any, key: any, val: any) => {
|
||||
@@ -98,6 +101,9 @@ export function installCompatInstanceProperties(map: PublicPropertiesMap) {
|
||||
_b: () => legacyBindObjectProps,
|
||||
_e: () => createCommentVNode,
|
||||
_v: () => createTextVNode,
|
||||
_m: i => legacyRenderStatic.bind(null, i)
|
||||
_m: i => legacyRenderStatic.bind(null, i),
|
||||
_g: () => legacyBindObjectListeners,
|
||||
_u: () => legacyresolveScopedSlots,
|
||||
_k: i => legacyCheckKeyCodes.bind(null, i)
|
||||
} as PublicPropertiesMap)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,39 @@
|
||||
import { isArray } from '@vue/shared'
|
||||
import { inject } from '../apiInject'
|
||||
import { ComponentInternalInstance, Data } from '../component'
|
||||
import { ComponentOptions, resolveMergedOptions } from '../componentOptions'
|
||||
import { DeprecationTypes, warnDeprecation } from './compatConfig'
|
||||
|
||||
export function createPropsDefaultThis(propKey: string) {
|
||||
export function createPropsDefaultThis(
|
||||
instance: ComponentInternalInstance,
|
||||
rawProps: Data,
|
||||
propKey: string
|
||||
) {
|
||||
return new Proxy(
|
||||
{},
|
||||
{
|
||||
get() {
|
||||
warnDeprecation(DeprecationTypes.PROPS_DEFAULT_THIS, null, propKey)
|
||||
get(_, key: string) {
|
||||
__DEV__ &&
|
||||
warnDeprecation(DeprecationTypes.PROPS_DEFAULT_THIS, null, propKey)
|
||||
// $options
|
||||
if (key === '$options') {
|
||||
return resolveMergedOptions(instance)
|
||||
}
|
||||
// props
|
||||
if (key in rawProps) {
|
||||
return rawProps[key]
|
||||
}
|
||||
// injections
|
||||
const injections = (instance.type as ComponentOptions).inject
|
||||
if (injections) {
|
||||
if (isArray(injections)) {
|
||||
if (injections.includes(key)) {
|
||||
return inject(key)
|
||||
}
|
||||
} else if (key in injections) {
|
||||
return inject(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
extend,
|
||||
hyphenate,
|
||||
isArray,
|
||||
isObject,
|
||||
makeMap,
|
||||
normalizeClass,
|
||||
normalizeStyle,
|
||||
ShapeFlags,
|
||||
@@ -14,6 +16,7 @@ import {
|
||||
Data,
|
||||
InternalRenderFunction
|
||||
} from '../component'
|
||||
import { currentRenderingInstance } from '../componentRenderContext'
|
||||
import { DirectiveArguments, withDirectives } from '../directives'
|
||||
import {
|
||||
resolveDirective,
|
||||
@@ -27,7 +30,12 @@ import {
|
||||
VNodeArrayChildren,
|
||||
VNodeProps
|
||||
} from '../vnode'
|
||||
import { checkCompatEnabled, DeprecationTypes } from './compatConfig'
|
||||
import {
|
||||
checkCompatEnabled,
|
||||
DeprecationTypes,
|
||||
isCompatEnabled
|
||||
} from './compatConfig'
|
||||
import { compatModelEventPrefix } from './vModel'
|
||||
|
||||
const v3CompiledRenderFnRE = /^(?:function \w+)?\(_ctx, _cache/
|
||||
|
||||
@@ -76,6 +84,11 @@ interface LegacyVNodeProps {
|
||||
props?: Record<string, unknown>
|
||||
slot?: string
|
||||
scopedSlots?: Record<string, Function>
|
||||
model?: {
|
||||
value: any
|
||||
callback: (v: any) => void
|
||||
expression: string
|
||||
}
|
||||
}
|
||||
|
||||
interface LegacyVNodeDirective {
|
||||
@@ -107,12 +120,22 @@ export function compatH(
|
||||
propsOrChildren?: any,
|
||||
children?: any
|
||||
): VNode {
|
||||
// to support v2 string component name lookup
|
||||
type = resolveDynamicComponent(type)
|
||||
// to support v2 string component name look!up
|
||||
if (typeof type === 'string') {
|
||||
const t = hyphenate(type)
|
||||
if (t === 'transition' || t === 'transition-group' || t === 'keep-alive') {
|
||||
// since transition and transition-group are runtime-dom-specific,
|
||||
// we cannot import them directly here. Instead they are registered using
|
||||
// special keys in @vue/compat entry.
|
||||
type = `__compat__${t}`
|
||||
}
|
||||
type = resolveDynamicComponent(type)
|
||||
}
|
||||
|
||||
const l = arguments.length
|
||||
if (l === 2) {
|
||||
if (isObject(propsOrChildren) && !isArray(propsOrChildren)) {
|
||||
const is2ndArgArrayChildren = isArray(propsOrChildren)
|
||||
if (l === 2 || is2ndArgArrayChildren) {
|
||||
if (isObject(propsOrChildren) && !is2ndArgArrayChildren) {
|
||||
// single vnode without props
|
||||
if (isVNode(propsOrChildren)) {
|
||||
return convertLegacySlots(createVNode(type, null, [propsOrChildren]))
|
||||
@@ -120,7 +143,7 @@ export function compatH(
|
||||
// props without children
|
||||
return convertLegacySlots(
|
||||
convertLegacyDirectives(
|
||||
createVNode(type, convertLegacyProps(propsOrChildren)),
|
||||
createVNode(type, convertLegacyProps(propsOrChildren, type)),
|
||||
propsOrChildren
|
||||
)
|
||||
)
|
||||
@@ -134,15 +157,20 @@ export function compatH(
|
||||
}
|
||||
return convertLegacySlots(
|
||||
convertLegacyDirectives(
|
||||
createVNode(type, convertLegacyProps(propsOrChildren), children),
|
||||
createVNode(type, convertLegacyProps(propsOrChildren, type), children),
|
||||
propsOrChildren
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const skipLegacyRootLevelProps = /*#__PURE__*/ makeMap(
|
||||
'refInFor,staticStyle,staticClass,directives,model'
|
||||
)
|
||||
|
||||
function convertLegacyProps(
|
||||
legacyProps?: LegacyVNodeProps
|
||||
legacyProps: LegacyVNodeProps | undefined,
|
||||
type: any
|
||||
): Data & VNodeProps | null {
|
||||
if (!legacyProps) {
|
||||
return null
|
||||
@@ -172,11 +200,7 @@ function convertLegacyProps(
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (
|
||||
key !== 'refInFor' &&
|
||||
key !== 'staticStyle' &&
|
||||
key !== 'staticClass'
|
||||
) {
|
||||
} else if (!skipLegacyRootLevelProps(key)) {
|
||||
converted[key] = legacyProps[key as keyof LegacyVNodeProps]
|
||||
}
|
||||
}
|
||||
@@ -188,6 +212,13 @@ function convertLegacyProps(
|
||||
converted.style = normalizeStyle([legacyProps.staticStyle, converted.style])
|
||||
}
|
||||
|
||||
if (legacyProps.model && isObject(type)) {
|
||||
// v2 compiled component v-model
|
||||
const { prop = 'value', event = 'input' } = (type as any).model || {}
|
||||
converted[prop] = legacyProps.model.value
|
||||
converted[compatModelEventPrefix + event] = legacyProps.model.callback
|
||||
}
|
||||
|
||||
return converted
|
||||
}
|
||||
|
||||
@@ -237,7 +268,12 @@ function convertLegacySlots(vnode: VNode): VNode {
|
||||
const child = children[i]
|
||||
const slotName =
|
||||
(isVNode(child) && child.props && child.props.slot) || 'default'
|
||||
;(slots[slotName] || (slots[slotName] = [] as any[])).push(child)
|
||||
const slot = slots[slotName] || (slots[slotName] = [] as any[])
|
||||
if (isVNode(child) && child.type === 'template') {
|
||||
slot.push(child.children)
|
||||
} else {
|
||||
slot.push(child)
|
||||
}
|
||||
}
|
||||
if (slots) {
|
||||
for (const key in slots) {
|
||||
@@ -263,3 +299,31 @@ function convertLegacySlots(vnode: VNode): VNode {
|
||||
|
||||
return vnode
|
||||
}
|
||||
|
||||
export function defineLegacyVNodeProperties(vnode: VNode) {
|
||||
if (
|
||||
isCompatEnabled(DeprecationTypes.RENDER_FUNCTION, currentRenderingInstance)
|
||||
) {
|
||||
const getInstance = () => vnode.component && vnode.component.proxy
|
||||
let componentOptions: any
|
||||
Object.defineProperties(vnode, {
|
||||
elm: { get: () => vnode.el },
|
||||
componentInstance: { get: getInstance },
|
||||
child: { get: getInstance },
|
||||
componentOptions: {
|
||||
get: () => {
|
||||
if (vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT) {
|
||||
if (componentOptions) {
|
||||
return componentOptions
|
||||
}
|
||||
return (componentOptions = {
|
||||
Ctor: vnode.type,
|
||||
propsData: vnode.props,
|
||||
children: vnode.children
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,22 @@ import {
|
||||
normalizeClass
|
||||
} from '@vue/shared'
|
||||
import { ComponentInternalInstance } from '../component'
|
||||
import { Slot } from '../componentSlots'
|
||||
import { createSlots } from '../helpers/createSlots'
|
||||
import { renderSlot } from '../helpers/renderSlot'
|
||||
import { toHandlers } from '../helpers/toHandlers'
|
||||
import { mergeProps, VNode } from '../vnode'
|
||||
|
||||
function toObject(arr: Array<any>): Object {
|
||||
const res = {}
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (arr[i]) {
|
||||
extend(res, arr[i])
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
export function legacyBindObjectProps(
|
||||
data: any,
|
||||
_tag: string,
|
||||
@@ -49,6 +62,10 @@ export function legacyBindObjectProps(
|
||||
return data
|
||||
}
|
||||
|
||||
export function legacyBindObjectListeners(props: any, listeners: any) {
|
||||
return mergeProps(props, toHandlers(listeners))
|
||||
}
|
||||
|
||||
export function legacyRenderSlot(
|
||||
instance: ComponentInternalInstance,
|
||||
name: string,
|
||||
@@ -62,6 +79,41 @@ export function legacyRenderSlot(
|
||||
return renderSlot(instance.slots, name, props, fallback && (() => fallback))
|
||||
}
|
||||
|
||||
type LegacyScopedSlotsData = Array<
|
||||
| {
|
||||
key: string
|
||||
fn: Function
|
||||
}
|
||||
| LegacyScopedSlotsData
|
||||
>
|
||||
|
||||
export function legacyresolveScopedSlots(
|
||||
fns: LegacyScopedSlotsData,
|
||||
raw?: Record<string, Slot>,
|
||||
// the following are added in 2.6
|
||||
hasDynamicKeys?: boolean
|
||||
) {
|
||||
// v2 default slot doesn't have name
|
||||
return createSlots(
|
||||
raw || ({ $stable: !hasDynamicKeys } as any),
|
||||
mapKeyToName(fns)
|
||||
)
|
||||
}
|
||||
|
||||
function mapKeyToName(slots: LegacyScopedSlotsData) {
|
||||
for (let i = 0; i < slots.length; i++) {
|
||||
const fn = slots[i]
|
||||
if (fn) {
|
||||
if (isArray(fn)) {
|
||||
mapKeyToName(fn)
|
||||
} else {
|
||||
;(fn as any).name = fn.key || 'default'
|
||||
}
|
||||
}
|
||||
}
|
||||
return slots as any
|
||||
}
|
||||
|
||||
const staticCacheMap = /*#__PURE__*/ new WeakMap<
|
||||
ComponentInternalInstance,
|
||||
any[]
|
||||
@@ -83,12 +135,30 @@ export function legacyRenderStatic(
|
||||
return (cache[index] = fn.call(ctx, null, ctx))
|
||||
}
|
||||
|
||||
function toObject(arr: Array<any>): Object {
|
||||
const res = {}
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (arr[i]) {
|
||||
extend(res, arr[i])
|
||||
}
|
||||
export function legacyCheckKeyCodes(
|
||||
instance: ComponentInternalInstance,
|
||||
eventKeyCode: number,
|
||||
key: string,
|
||||
builtInKeyCode?: number | number[],
|
||||
eventKeyName?: string,
|
||||
builtInKeyName?: string | string[]
|
||||
) {
|
||||
const config = instance.appContext.config as any
|
||||
const configKeyCodes = config.keyCodes || {}
|
||||
const mappedKeyCode = configKeyCodes[key] || builtInKeyCode
|
||||
if (builtInKeyName && eventKeyName && !configKeyCodes[key]) {
|
||||
return isKeyNotMatch(builtInKeyName, eventKeyName)
|
||||
} else if (mappedKeyCode) {
|
||||
return isKeyNotMatch(mappedKeyCode, eventKeyCode)
|
||||
} else if (eventKeyName) {
|
||||
return hyphenate(eventKeyName) !== key
|
||||
}
|
||||
}
|
||||
|
||||
function isKeyNotMatch<T>(expect: T | T[], actual: T): boolean {
|
||||
if (isArray(expect)) {
|
||||
return expect.indexOf(actual) === -1
|
||||
} else {
|
||||
return expect !== actual
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
@@ -35,6 +35,9 @@ export function convertLegacyVModelProps(vnode: VNode) {
|
||||
warnedTypes.add(type as ComponentOptions)
|
||||
}
|
||||
|
||||
// v3 compiled model code -> v2 compat props
|
||||
// modelValue -> value
|
||||
// onUpdate:modelValue -> onModelCompat:input
|
||||
const { prop = 'value', event = 'input' } = (type as any).model || {}
|
||||
props[prop] = props.modelValue
|
||||
delete props.modelValue
|
||||
@@ -42,7 +45,6 @@ export function convertLegacyVModelProps(vnode: VNode) {
|
||||
if (dynamicProps) {
|
||||
dynamicProps[dynamicProps.indexOf('modelValue')] = prop
|
||||
}
|
||||
|
||||
props[compatModelEventPrefix + event] = props['onUpdate:modelValue']
|
||||
delete props['onUpdate:modelValue']
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user