wip: compat integration progress
This commit is contained in:
parent
7dc681c196
commit
f6dee53270
@ -203,9 +203,10 @@ const deprecationData: Record<DeprecationTypes, DeprecationData> = {
|
|||||||
|
|
||||||
[DeprecationTypes.INSTANCE_LISTENERS]: {
|
[DeprecationTypes.INSTANCE_LISTENERS]: {
|
||||||
message:
|
message:
|
||||||
`vm.$listeners has been removed. Parent v-on listeners are now ` +
|
`vm.$listeners has been removed. In Vue 3, parent v-on listeners are ` +
|
||||||
`included in vm.$attrs and it is no longer necessary to separately use ` +
|
`included in vm.$attrs and it is no longer necessary to separately use ` +
|
||||||
`v-on="$listeners" if you are already using v-bind="$attrs".`,
|
`v-on="$listeners" if you are already using v-bind="$attrs". ` +
|
||||||
|
`(Note: the Vue 3 behavior only applies if this compat config is disabled)`,
|
||||||
link: `https://v3.vuejs.org/guide/migration/listeners-removed.html`
|
link: `https://v3.vuejs.org/guide/migration/listeners-removed.html`
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { extend, NOOP } from '@vue/shared'
|
import { extend, NOOP, toDisplayString, toNumber } from '@vue/shared'
|
||||||
import { PublicPropertiesMap } from '../componentPublicInstance'
|
import { PublicPropertiesMap } from '../componentPublicInstance'
|
||||||
import { getCompatChildren } from './instanceChildren'
|
import { getCompatChildren } from './instanceChildren'
|
||||||
import {
|
import {
|
||||||
@ -11,6 +11,14 @@ import { off, on, once } from './instanceEventEmitter'
|
|||||||
import { getCompatListeners } from './instanceListeners'
|
import { getCompatListeners } from './instanceListeners'
|
||||||
import { shallowReadonly } from '@vue/reactivity'
|
import { shallowReadonly } from '@vue/reactivity'
|
||||||
import { legacySlotProxyHandlers } from './component'
|
import { legacySlotProxyHandlers } from './component'
|
||||||
|
import { compatH } from './renderFn'
|
||||||
|
import {
|
||||||
|
legacyBindObjectProps,
|
||||||
|
legacyRenderSlot,
|
||||||
|
legacyRenderStatic
|
||||||
|
} from './renderHelpers'
|
||||||
|
import { createCommentVNode, createTextVNode } from '../vnode'
|
||||||
|
import { renderList } from '../helpers/renderList'
|
||||||
|
|
||||||
export function installCompatInstanceProperties(map: PublicPropertiesMap) {
|
export function installCompatInstanceProperties(map: PublicPropertiesMap) {
|
||||||
const set = (target: any, key: any, val: any) => {
|
const set = (target: any, key: any, val: any) => {
|
||||||
@ -77,6 +85,19 @@ export function installCompatInstanceProperties(map: PublicPropertiesMap) {
|
|||||||
$off: i => off.bind(null, i),
|
$off: i => off.bind(null, i),
|
||||||
|
|
||||||
$children: getCompatChildren,
|
$children: getCompatChildren,
|
||||||
$listeners: getCompatListeners
|
$listeners: getCompatListeners,
|
||||||
|
|
||||||
|
// v2 render helpers
|
||||||
|
$createElement: () => compatH,
|
||||||
|
_self: i => i.proxy,
|
||||||
|
_c: () => compatH,
|
||||||
|
_n: () => toNumber,
|
||||||
|
_s: () => toDisplayString,
|
||||||
|
_l: () => renderList,
|
||||||
|
_t: i => legacyRenderSlot.bind(null, i),
|
||||||
|
_b: () => legacyBindObjectProps,
|
||||||
|
_e: () => createCommentVNode,
|
||||||
|
_v: () => createTextVNode,
|
||||||
|
_m: i => legacyRenderStatic.bind(null, i)
|
||||||
} as PublicPropertiesMap)
|
} as PublicPropertiesMap)
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,8 @@ import {
|
|||||||
} from '../vnode'
|
} from '../vnode'
|
||||||
import { checkCompatEnabled, DeprecationTypes } from './compatConfig'
|
import { checkCompatEnabled, DeprecationTypes } from './compatConfig'
|
||||||
|
|
||||||
|
const v3CompiledRenderFnRE = /^(?:function \w+)?\(_ctx, _cache/
|
||||||
|
|
||||||
export function convertLegacyRenderFn(instance: ComponentInternalInstance) {
|
export function convertLegacyRenderFn(instance: ComponentInternalInstance) {
|
||||||
const Component = instance.type as ComponentOptions
|
const Component = instance.type as ComponentOptions
|
||||||
const render = Component.render as InternalRenderFunction | undefined
|
const render = Component.render as InternalRenderFunction | undefined
|
||||||
@ -38,8 +40,7 @@ export function convertLegacyRenderFn(instance: ComponentInternalInstance) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const string = render.toString()
|
if (v3CompiledRenderFnRE.test(render.toString())) {
|
||||||
if (string.startsWith('function render(_ctx') || string.startsWith('(_ctx')) {
|
|
||||||
// v3 pre-compiled function
|
// v3 pre-compiled function
|
||||||
render._compatChecked = true
|
render._compatChecked = true
|
||||||
return
|
return
|
||||||
@ -128,9 +129,7 @@ export function compatH(
|
|||||||
return convertLegacySlots(createVNode(type, null, propsOrChildren))
|
return convertLegacySlots(createVNode(type, null, propsOrChildren))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (l > 3) {
|
if (isVNode(children)) {
|
||||||
children = Array.prototype.slice.call(arguments, 2)
|
|
||||||
} else if (l === 3 && isVNode(children)) {
|
|
||||||
children = [children]
|
children = [children]
|
||||||
}
|
}
|
||||||
return convertLegacySlots(
|
return convertLegacySlots(
|
||||||
@ -157,13 +156,20 @@ function convertLegacyProps(
|
|||||||
} else if (key === 'on' || key === 'nativeOn') {
|
} else if (key === 'on' || key === 'nativeOn') {
|
||||||
const listeners = legacyProps[key]
|
const listeners = legacyProps[key]
|
||||||
for (const event in listeners) {
|
for (const event in listeners) {
|
||||||
const handlerKey = toHandlerKey(event)
|
const handlerKey = convertLegacyEventKey(event)
|
||||||
const existing = converted[handlerKey]
|
const existing = converted[handlerKey]
|
||||||
const incoming = listeners[event]
|
const incoming = listeners[event]
|
||||||
if (existing !== incoming) {
|
if (existing !== incoming) {
|
||||||
converted[handlerKey] = existing
|
if (existing) {
|
||||||
? [].concat(existing as any, incoming as any)
|
// for the rare case where the same handler is attached
|
||||||
: incoming
|
// twice with/without .native modifier...
|
||||||
|
if (key === 'nativeOn' && String(existing) === String(incoming)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
converted[handlerKey] = [].concat(existing as any, incoming as any)
|
||||||
|
} else {
|
||||||
|
converted[handlerKey] = incoming
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (
|
} else if (
|
||||||
@ -185,6 +191,20 @@ function convertLegacyProps(
|
|||||||
return converted
|
return converted
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function convertLegacyEventKey(event: string): string {
|
||||||
|
// normalize v2 event prefixes
|
||||||
|
if (event[0] === '&') {
|
||||||
|
event = event.slice(1) + 'Passive'
|
||||||
|
}
|
||||||
|
if (event[0] === '~') {
|
||||||
|
event = event.slice(1) + 'Once'
|
||||||
|
}
|
||||||
|
if (event[0] === '!') {
|
||||||
|
event = event.slice(1) + 'Capture'
|
||||||
|
}
|
||||||
|
return toHandlerKey(event)
|
||||||
|
}
|
||||||
|
|
||||||
function convertLegacyDirectives(
|
function convertLegacyDirectives(
|
||||||
vnode: VNode,
|
vnode: VNode,
|
||||||
props?: LegacyVNodeProps
|
props?: LegacyVNodeProps
|
||||||
|
94
packages/runtime-core/src/compat/renderHelpers.ts
Normal file
94
packages/runtime-core/src/compat/renderHelpers.ts
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
import {
|
||||||
|
camelize,
|
||||||
|
extend,
|
||||||
|
hyphenate,
|
||||||
|
isArray,
|
||||||
|
isObject,
|
||||||
|
isReservedProp,
|
||||||
|
normalizeClass
|
||||||
|
} from '@vue/shared'
|
||||||
|
import { ComponentInternalInstance } from '../component'
|
||||||
|
import { renderSlot } from '../helpers/renderSlot'
|
||||||
|
import { mergeProps, VNode } from '../vnode'
|
||||||
|
|
||||||
|
export function legacyBindObjectProps(
|
||||||
|
data: any,
|
||||||
|
_tag: string,
|
||||||
|
value: any,
|
||||||
|
_asProp: boolean,
|
||||||
|
isSync?: boolean
|
||||||
|
) {
|
||||||
|
if (value && isObject(value)) {
|
||||||
|
if (isArray(value)) {
|
||||||
|
value = toObject(value)
|
||||||
|
}
|
||||||
|
for (const key in value) {
|
||||||
|
if (isReservedProp(key)) {
|
||||||
|
data[key] = value[key]
|
||||||
|
} else if (key === 'class') {
|
||||||
|
data.class = normalizeClass([data.class, value.class])
|
||||||
|
} else if (key === 'style') {
|
||||||
|
data.style = normalizeClass([data.style, value.style])
|
||||||
|
} else {
|
||||||
|
const attrs = data.attrs || (data.attrs = {})
|
||||||
|
const camelizedKey = camelize(key)
|
||||||
|
const hyphenatedKey = hyphenate(key)
|
||||||
|
if (!(camelizedKey in attrs) && !(hyphenatedKey in attrs)) {
|
||||||
|
attrs[key] = value[key]
|
||||||
|
|
||||||
|
if (isSync) {
|
||||||
|
const on = data.on || (data.on = {})
|
||||||
|
on[`update:${key}`] = function($event: any) {
|
||||||
|
value[key] = $event
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
export function legacyRenderSlot(
|
||||||
|
instance: ComponentInternalInstance,
|
||||||
|
name: string,
|
||||||
|
fallback?: VNode[],
|
||||||
|
props?: any,
|
||||||
|
bindObject?: any
|
||||||
|
) {
|
||||||
|
if (bindObject) {
|
||||||
|
props = mergeProps(props, bindObject)
|
||||||
|
}
|
||||||
|
return renderSlot(instance.slots, name, props, fallback && (() => fallback))
|
||||||
|
}
|
||||||
|
|
||||||
|
const staticCacheMap = /*#__PURE__*/ new WeakMap<
|
||||||
|
ComponentInternalInstance,
|
||||||
|
any[]
|
||||||
|
>()
|
||||||
|
|
||||||
|
export function legacyRenderStatic(
|
||||||
|
instance: ComponentInternalInstance,
|
||||||
|
index: number
|
||||||
|
) {
|
||||||
|
let cache = staticCacheMap.get(instance)
|
||||||
|
if (!cache) {
|
||||||
|
staticCacheMap.set(instance, (cache = []))
|
||||||
|
}
|
||||||
|
if (cache[index]) {
|
||||||
|
return cache[index]
|
||||||
|
}
|
||||||
|
const fn = (instance.type as any).staticRenderFns[index]
|
||||||
|
const ctx = instance.proxy
|
||||||
|
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])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
@ -9,11 +9,6 @@ import {
|
|||||||
isCompatEnabled
|
isCompatEnabled
|
||||||
} from './compatConfig'
|
} from './compatConfig'
|
||||||
|
|
||||||
const defaultModelMapping = {
|
|
||||||
prop: 'value',
|
|
||||||
event: 'input'
|
|
||||||
}
|
|
||||||
|
|
||||||
export const compatModelEventPrefix = `onModelCompat:`
|
export const compatModelEventPrefix = `onModelCompat:`
|
||||||
|
|
||||||
const warnedTypes = new WeakSet()
|
const warnedTypes = new WeakSet()
|
||||||
@ -40,7 +35,7 @@ export function convertLegacyVModelProps(vnode: VNode) {
|
|||||||
warnedTypes.add(type as ComponentOptions)
|
warnedTypes.add(type as ComponentOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
const { prop, event } = (type as any).model || defaultModelMapping
|
const { prop = 'value', event = 'input' } = (type as any).model || {}
|
||||||
props[prop] = props.modelValue
|
props[prop] = props.modelValue
|
||||||
delete props.modelValue
|
delete props.modelValue
|
||||||
// important: update dynamic props
|
// important: update dynamic props
|
||||||
|
@ -20,7 +20,8 @@ import {
|
|||||||
isReservedProp,
|
isReservedProp,
|
||||||
EMPTY_ARR,
|
EMPTY_ARR,
|
||||||
def,
|
def,
|
||||||
extend
|
extend,
|
||||||
|
isOn
|
||||||
} from '@vue/shared'
|
} from '@vue/shared'
|
||||||
import { warn } from './warning'
|
import { warn } from './warning'
|
||||||
import {
|
import {
|
||||||
@ -224,6 +225,13 @@ export function updateProps(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (
|
||||||
|
__COMPAT__ &&
|
||||||
|
isOn(key) &&
|
||||||
|
isCompatEnabled(DeprecationTypes.INSTANCE_LISTENERS, instance)
|
||||||
|
) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
attrs[key] = value
|
attrs[key] = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -320,6 +328,13 @@ function setFullProps(
|
|||||||
// Any non-declared (either as a prop or an emitted event) props are put
|
// Any non-declared (either as a prop or an emitted event) props are put
|
||||||
// into a separate `attrs` object for spreading. Make sure to preserve
|
// into a separate `attrs` object for spreading. Make sure to preserve
|
||||||
// original key casing
|
// original key casing
|
||||||
|
if (
|
||||||
|
__COMPAT__ &&
|
||||||
|
isOn(key) &&
|
||||||
|
isCompatEnabled(DeprecationTypes.INSTANCE_LISTENERS, instance)
|
||||||
|
) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
attrs[key] = value
|
attrs[key] = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,7 @@ const normalizeObjectSlots = (rawSlots: RawSlots, slots: InternalSlots) => {
|
|||||||
if (isFunction(value)) {
|
if (isFunction(value)) {
|
||||||
slots[key] = normalizeSlot(key, value, ctx)
|
slots[key] = normalizeSlot(key, value, ctx)
|
||||||
} else if (value != null) {
|
} else if (value != null) {
|
||||||
if (__DEV__) {
|
if (__DEV__ && !__COMPAT__) {
|
||||||
warn(
|
warn(
|
||||||
`Non-function value encountered for slot "${key}". ` +
|
`Non-function value encountered for slot "${key}". ` +
|
||||||
`Prefer function slots for better performance.`
|
`Prefer function slots for better performance.`
|
||||||
|
3
packages/vue-compat/src/esm-index.ts
Normal file
3
packages/vue-compat/src/esm-index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import Vue from './index'
|
||||||
|
export default Vue
|
||||||
|
export * from '@vue/runtime-dom'
|
3
packages/vue-compat/src/esm-runtime.ts
Normal file
3
packages/vue-compat/src/esm-runtime.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import Vue from './runtime'
|
||||||
|
export default Vue
|
||||||
|
export * from '@vue/runtime-dom'
|
@ -116,7 +116,16 @@ function createConfig(format, output, plugins = []) {
|
|||||||
// during a single build.
|
// during a single build.
|
||||||
hasTSChecked = true
|
hasTSChecked = true
|
||||||
|
|
||||||
const entryFile = /runtime$/.test(format) ? `src/runtime.ts` : `src/index.ts`
|
let entryFile = /runtime$/.test(format) ? `src/runtime.ts` : `src/index.ts`
|
||||||
|
|
||||||
|
// the compat build needs both default AND named exports. This will cause
|
||||||
|
// Rollup to complain for non-ESM targets, so we use separate entries for
|
||||||
|
// esm vs. non-esm builds.
|
||||||
|
if (isCompatBuild && (isBrowserESMBuild || isBundlerESMBuild)) {
|
||||||
|
entryFile = /runtime$/.test(format)
|
||||||
|
? `src/esm-runtime.ts`
|
||||||
|
: `src/esm-index.ts`
|
||||||
|
}
|
||||||
|
|
||||||
let external = []
|
let external = []
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user