wip: render function compat
This commit is contained in:
parent
457a56e331
commit
f05d6dfd98
@ -1,16 +0,0 @@
|
||||
import Vue from '@vue/compat'
|
||||
|
||||
test('should work', async () => {
|
||||
const el = document.createElement('div')
|
||||
el.innerHTML = `{{ msg }}`
|
||||
new Vue({
|
||||
el,
|
||||
data() {
|
||||
return {
|
||||
msg: 'hello'
|
||||
}
|
||||
}
|
||||
})
|
||||
expect('global app bootstrapping API has changed').toHaveBeenWarned()
|
||||
expect(el.innerHTML).toBe('hello')
|
||||
})
|
18
packages/runtime-core/src/compat/__tests__/global.spec.ts
Normal file
18
packages/runtime-core/src/compat/__tests__/global.spec.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import Vue from '@vue/compat'
|
||||
|
||||
describe('compat: global API', () => {
|
||||
test('should work', async () => {
|
||||
const el = document.createElement('div')
|
||||
el.innerHTML = `{{ msg }}`
|
||||
new Vue({
|
||||
el,
|
||||
data() {
|
||||
return {
|
||||
msg: 'hello'
|
||||
}
|
||||
}
|
||||
})
|
||||
expect('global app bootstrapping API has changed').toHaveBeenWarned()
|
||||
expect(el.innerHTML).toBe('hello')
|
||||
})
|
||||
})
|
149
packages/runtime-core/src/compat/__tests__/renderFn.spec.ts
Normal file
149
packages/runtime-core/src/compat/__tests__/renderFn.spec.ts
Normal file
@ -0,0 +1,149 @@
|
||||
import { ShapeFlags } from '@vue/shared/src'
|
||||
import { createComponentInstance } from '../../component'
|
||||
import { setCurrentRenderingInstance } from '../../componentRenderContext'
|
||||
import { DirectiveBinding } from '../../directives'
|
||||
import { createVNode } from '../../vnode'
|
||||
import { compatH as h } from '../renderFn'
|
||||
|
||||
describe('compat: render function', () => {
|
||||
const mockDir = {}
|
||||
const mockChildComp = {}
|
||||
const mockComponent = {
|
||||
directives: {
|
||||
mockDir
|
||||
},
|
||||
components: {
|
||||
foo: mockChildComp
|
||||
}
|
||||
}
|
||||
const mockInstance = createComponentInstance(
|
||||
createVNode(mockComponent),
|
||||
null,
|
||||
null
|
||||
)
|
||||
beforeEach(() => {
|
||||
setCurrentRenderingInstance(mockInstance)
|
||||
})
|
||||
afterEach(() => {
|
||||
setCurrentRenderingInstance(null)
|
||||
})
|
||||
|
||||
test('string component lookup', () => {
|
||||
expect(h('foo')).toMatchObject({
|
||||
type: mockChildComp
|
||||
})
|
||||
})
|
||||
|
||||
test('class / style / attrs / domProps / props', () => {
|
||||
expect(
|
||||
h('div', {
|
||||
class: 'foo',
|
||||
style: { color: 'red' },
|
||||
attrs: {
|
||||
id: 'foo'
|
||||
},
|
||||
domProps: {
|
||||
innerHTML: 'hi'
|
||||
},
|
||||
props: {
|
||||
myProp: 'foo'
|
||||
}
|
||||
})
|
||||
).toMatchObject({
|
||||
props: {
|
||||
class: 'foo',
|
||||
style: { color: 'red' },
|
||||
id: 'foo',
|
||||
innerHTML: 'hi',
|
||||
myProp: 'foo'
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
test('on / nativeOn', () => {
|
||||
const fn = () => {}
|
||||
expect(
|
||||
h('div', {
|
||||
on: {
|
||||
click: fn,
|
||||
fooBar: fn
|
||||
},
|
||||
nativeOn: {
|
||||
click: fn,
|
||||
'bar-baz': fn
|
||||
}
|
||||
})
|
||||
).toMatchObject({
|
||||
props: {
|
||||
onClick: fn, // should dedupe
|
||||
onFooBar: fn,
|
||||
'onBar-baz': fn
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
test('directives', () => {
|
||||
expect(
|
||||
h('div', {
|
||||
directives: [
|
||||
{
|
||||
name: 'mock-dir',
|
||||
value: '2',
|
||||
// expression: '1 + 1',
|
||||
arg: 'foo',
|
||||
modifiers: {
|
||||
bar: true
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
).toMatchObject({
|
||||
dirs: [
|
||||
{
|
||||
dir: mockDir,
|
||||
instance: mockInstance.proxy,
|
||||
value: '2',
|
||||
oldValue: void 0,
|
||||
arg: 'foo',
|
||||
modifiers: {
|
||||
bar: true
|
||||
}
|
||||
}
|
||||
] as DirectiveBinding[]
|
||||
})
|
||||
})
|
||||
|
||||
test('scopedSlots', () => {
|
||||
const scopedSlots = {
|
||||
default() {}
|
||||
}
|
||||
const vnode = h(mockComponent, {
|
||||
scopedSlots
|
||||
})
|
||||
expect(vnode).toMatchObject({
|
||||
children: scopedSlots
|
||||
})
|
||||
expect('scopedSlots' in vnode.props!).toBe(false)
|
||||
expect(vnode.shapeFlag & ShapeFlags.SLOTS_CHILDREN).toBeTruthy()
|
||||
})
|
||||
|
||||
test('legacy named slot', () => {
|
||||
const vnode = h(mockComponent, [
|
||||
'text',
|
||||
h('div', { slot: 'foo' }, 'one'),
|
||||
h('div', { slot: 'bar' }, 'two'),
|
||||
h('div', { slot: 'foo' }, 'three'),
|
||||
h('div', 'four')
|
||||
])
|
||||
expect(vnode.shapeFlag & ShapeFlags.SLOTS_CHILDREN).toBeTruthy()
|
||||
const slots = vnode.children as any
|
||||
|
||||
// default
|
||||
expect(slots.default()).toMatchObject(['text', { children: 'four' }])
|
||||
expect(slots.foo()).toMatchObject([
|
||||
{ children: 'one' },
|
||||
{ children: 'three' }
|
||||
])
|
||||
expect(slots.bar()).toMatchObject([{ children: 'two' }])
|
||||
})
|
||||
})
|
@ -1,5 +1,5 @@
|
||||
import { extend } from '@vue/shared'
|
||||
import { ComponentOptions, getCurrentInstance } from '../component'
|
||||
import { ComponentInternalInstance, ComponentOptions } from '../component'
|
||||
import { DeprecationTypes, warnDeprecation } from './deprecations'
|
||||
|
||||
export type CompatConfig = Partial<
|
||||
@ -14,8 +14,10 @@ export function configureCompat(config: CompatConfig) {
|
||||
extend(globalCompatConfig, config)
|
||||
}
|
||||
|
||||
export function getCompatConfigForKey(key: DeprecationTypes | 'MODE') {
|
||||
const instance = getCurrentInstance()
|
||||
export function getCompatConfigForKey(
|
||||
key: DeprecationTypes | 'MODE',
|
||||
instance: ComponentInternalInstance | null
|
||||
) {
|
||||
const instanceConfig =
|
||||
instance && (instance.type as ComponentOptions).compatConfig
|
||||
if (instanceConfig && key in instanceConfig) {
|
||||
@ -24,9 +26,12 @@ export function getCompatConfigForKey(key: DeprecationTypes | 'MODE') {
|
||||
return globalCompatConfig[key]
|
||||
}
|
||||
|
||||
export function isCompatEnabled(key: DeprecationTypes): boolean {
|
||||
const mode = getCompatConfigForKey('MODE') || 2
|
||||
const val = getCompatConfigForKey(key)
|
||||
export function isCompatEnabled(
|
||||
key: DeprecationTypes,
|
||||
instance: ComponentInternalInstance | null
|
||||
): boolean {
|
||||
const mode = getCompatConfigForKey('MODE', instance) || 2
|
||||
const val = getCompatConfigForKey(key, instance)
|
||||
if (mode === 2) {
|
||||
return val !== false
|
||||
} else {
|
||||
@ -34,19 +39,27 @@ export function isCompatEnabled(key: DeprecationTypes): boolean {
|
||||
}
|
||||
}
|
||||
|
||||
export function assertCompatEnabled(key: DeprecationTypes, ...args: any[]) {
|
||||
if (!isCompatEnabled(key)) {
|
||||
export function assertCompatEnabled(
|
||||
key: DeprecationTypes,
|
||||
instance: ComponentInternalInstance | null,
|
||||
...args: any[]
|
||||
) {
|
||||
if (!isCompatEnabled(key, instance)) {
|
||||
throw new Error(`${key} compat has been disabled.`)
|
||||
} else if (__DEV__) {
|
||||
warnDeprecation(key, ...args)
|
||||
warnDeprecation(key, instance, ...args)
|
||||
}
|
||||
}
|
||||
|
||||
export function softAssertCompatEnabled(key: DeprecationTypes, ...args: any[]) {
|
||||
export function softAssertCompatEnabled(
|
||||
key: DeprecationTypes,
|
||||
instance: ComponentInternalInstance | null,
|
||||
...args: any[]
|
||||
) {
|
||||
if (__DEV__) {
|
||||
warnDeprecation(key, ...args)
|
||||
warnDeprecation(key, instance, ...args)
|
||||
}
|
||||
return isCompatEnabled(key)
|
||||
return isCompatEnabled(key, instance)
|
||||
}
|
||||
|
||||
// disable features that conflict with v3 behavior
|
||||
|
@ -2,6 +2,7 @@ import { isArray, isFunction, isObject, isPromise } from '@vue/shared'
|
||||
import { defineAsyncComponent } from '../apiAsyncComponent'
|
||||
import {
|
||||
Component,
|
||||
ComponentInternalInstance,
|
||||
ComponentOptions,
|
||||
FunctionalComponent,
|
||||
getCurrentInstance
|
||||
@ -14,12 +15,18 @@ import { DeprecationTypes, warnDeprecation } from './deprecations'
|
||||
import { getCompatListeners } from './instanceListeners'
|
||||
import { compatH } from './renderFn'
|
||||
|
||||
export function convertLegacyComponent(comp: any): Component {
|
||||
export function convertLegacyComponent(
|
||||
comp: any,
|
||||
instance: ComponentInternalInstance | null
|
||||
): Component {
|
||||
// 2.x async component
|
||||
// since after disabling this, plain functions are still valid usage, do not
|
||||
// use softAssert here.
|
||||
if (isFunction(comp) && isCompatEnabled(DeprecationTypes.COMPONENT_ASYNC)) {
|
||||
__DEV__ && warnDeprecation(DeprecationTypes.COMPONENT_ASYNC, comp)
|
||||
if (
|
||||
isFunction(comp) &&
|
||||
isCompatEnabled(DeprecationTypes.COMPONENT_ASYNC, instance)
|
||||
) {
|
||||
__DEV__ && warnDeprecation(DeprecationTypes.COMPONENT_ASYNC, instance, comp)
|
||||
return convertLegacyAsyncComponent(comp)
|
||||
}
|
||||
|
||||
@ -27,7 +34,11 @@ export function convertLegacyComponent(comp: any): Component {
|
||||
if (
|
||||
isObject(comp) &&
|
||||
comp.functional &&
|
||||
softAssertCompatEnabled(DeprecationTypes.COMPONENT_FUNCTIONAL, comp)
|
||||
softAssertCompatEnabled(
|
||||
DeprecationTypes.COMPONENT_FUNCTIONAL,
|
||||
instance,
|
||||
comp
|
||||
)
|
||||
) {
|
||||
return convertLegacyFunctionalComponent(comp)
|
||||
}
|
||||
@ -92,7 +103,7 @@ const normalizedFunctionalComponentMap = new Map<
|
||||
FunctionalComponent
|
||||
>()
|
||||
|
||||
const legacySlotProxyHandlers: ProxyHandler<InternalSlots> = {
|
||||
export const legacySlotProxyHandlers: ProxyHandler<InternalSlots> = {
|
||||
get(target, key: string) {
|
||||
const slot = target[key]
|
||||
return slot && slot()
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { isArray } from '@vue/shared'
|
||||
import { ComponentInternalInstance } from '../component'
|
||||
import { ObjectDirective, DirectiveHook } from '../directives'
|
||||
import { softAssertCompatEnabled } from './compatConfig'
|
||||
import { DeprecationTypes } from './deprecations'
|
||||
@ -25,7 +26,8 @@ const legacyDirectiveHookMap: Partial<
|
||||
|
||||
export function mapCompatDirectiveHook(
|
||||
name: keyof ObjectDirective,
|
||||
dir: ObjectDirective & LegacyDirective
|
||||
dir: ObjectDirective & LegacyDirective,
|
||||
instance: ComponentInternalInstance | null
|
||||
): DirectiveHook | DirectiveHook[] | undefined {
|
||||
const mappedName = legacyDirectiveHookMap[name]
|
||||
if (mappedName) {
|
||||
@ -34,14 +36,24 @@ export function mapCompatDirectiveHook(
|
||||
mappedName.forEach(name => {
|
||||
const mappedHook = dir[name]
|
||||
if (mappedHook) {
|
||||
softAssertCompatEnabled(DeprecationTypes.CUSTOM_DIR, mappedName, name)
|
||||
softAssertCompatEnabled(
|
||||
DeprecationTypes.CUSTOM_DIR,
|
||||
instance,
|
||||
mappedName,
|
||||
name
|
||||
)
|
||||
hook.push(mappedHook)
|
||||
}
|
||||
})
|
||||
return hook.length ? hook : undefined
|
||||
} else {
|
||||
if (dir[mappedName]) {
|
||||
softAssertCompatEnabled(DeprecationTypes.CUSTOM_DIR, mappedName, name)
|
||||
softAssertCompatEnabled(
|
||||
DeprecationTypes.CUSTOM_DIR,
|
||||
instance,
|
||||
mappedName,
|
||||
name
|
||||
)
|
||||
}
|
||||
return dir[mappedName]
|
||||
}
|
||||
|
@ -1,13 +1,19 @@
|
||||
import { isPlainObject } from '@vue/shared'
|
||||
import { ComponentInternalInstance } from '../component'
|
||||
import { DeprecationTypes, warnDeprecation } from './deprecations'
|
||||
|
||||
export function deepMergeData(to: any, from: any) {
|
||||
export function deepMergeData(
|
||||
to: any,
|
||||
from: any,
|
||||
instance: ComponentInternalInstance
|
||||
) {
|
||||
for (const key in from) {
|
||||
const toVal = to[key]
|
||||
const fromVal = from[key]
|
||||
if (key in to && isPlainObject(toVal) && isPlainObject(fromVal)) {
|
||||
__DEV__ && warnDeprecation(DeprecationTypes.OPTIONS_DATA_MERGE, key)
|
||||
deepMergeData(toVal, fromVal)
|
||||
__DEV__ &&
|
||||
warnDeprecation(DeprecationTypes.OPTIONS_DATA_MERGE, instance, key)
|
||||
deepMergeData(toVal, fromVal, instance)
|
||||
} else {
|
||||
to[key] = fromVal
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import {
|
||||
ComponentInternalInstance,
|
||||
formatComponentName,
|
||||
getComponentName,
|
||||
getCurrentInstance,
|
||||
@ -50,7 +51,9 @@ export const enum DeprecationTypes {
|
||||
TRANSITION_GROUP_ROOT = 'TRANSITION_GROUP_ROOT',
|
||||
|
||||
COMPONENT_ASYNC = 'COMPONENT_ASYNC',
|
||||
COMPONENT_FUNCTIONAL = 'COMPONENT_FUNCTIONAL'
|
||||
COMPONENT_FUNCTIONAL = 'COMPONENT_FUNCTIONAL',
|
||||
|
||||
RENDER_FUNCTION = 'RENDER_FUNCTION'
|
||||
}
|
||||
|
||||
type DeprecationData = {
|
||||
@ -340,25 +343,41 @@ const deprecationData: Record<DeprecationTypes, DeprecationData> = {
|
||||
)
|
||||
},
|
||||
link: `https://v3.vuejs.org/guide/migration/functional-components.html`
|
||||
},
|
||||
|
||||
[DeprecationTypes.RENDER_FUNCTION]: {
|
||||
message:
|
||||
`Vue 3's render function API has changed. ` +
|
||||
`You can opt-in to the new API with:` +
|
||||
`\n\n configureCompat({ ${
|
||||
DeprecationTypes.RENDER_FUNCTION
|
||||
}: false })\n` +
|
||||
`\n (This can also be done per-component via the "compatConfig" option.)`,
|
||||
link: `https://v3.vuejs.org/guide/migration/render-function-api.html`
|
||||
}
|
||||
}
|
||||
|
||||
const instanceWarned: Record<string, true> = Object.create(null)
|
||||
const warnCount: Record<string, number> = Object.create(null)
|
||||
|
||||
export function warnDeprecation(key: DeprecationTypes, ...args: any[]) {
|
||||
export function warnDeprecation(
|
||||
key: DeprecationTypes,
|
||||
instance: ComponentInternalInstance | null,
|
||||
...args: any[]
|
||||
) {
|
||||
if (!__DEV__) {
|
||||
return
|
||||
}
|
||||
|
||||
instance = instance || getCurrentInstance()
|
||||
|
||||
// check user config
|
||||
const config = getCompatConfigForKey(key)
|
||||
const config = getCompatConfigForKey(key, instance)
|
||||
if (config === 'suppress-warning') {
|
||||
return
|
||||
}
|
||||
|
||||
const dupKey = key + args.join('')
|
||||
const instance = getCurrentInstance()
|
||||
const compName = instance && formatComponentName(instance, instance.type)
|
||||
|
||||
// skip if the same warning is emitted for the same component type
|
||||
@ -385,7 +404,7 @@ export function warnDeprecation(key: DeprecationTypes, ...args: any[]) {
|
||||
typeof message === 'function' ? message(...args) : message
|
||||
}${link ? `\n Details: ${link}` : ``}`
|
||||
)
|
||||
if (!isCompatEnabled(key)) {
|
||||
if (!isCompatEnabled(key, instance)) {
|
||||
console.error(
|
||||
`^ The above deprecation's compat behavior is disabled and will likely ` +
|
||||
`lead to runtime errors.`
|
||||
|
@ -96,13 +96,13 @@ export function createCompatVue(
|
||||
const singletonApp = createApp({})
|
||||
|
||||
function createCompatApp(options: ComponentOptions = {}, Ctor: any) {
|
||||
assertCompatEnabled(DeprecationTypes.GLOBAL_MOUNT)
|
||||
assertCompatEnabled(DeprecationTypes.GLOBAL_MOUNT, null)
|
||||
|
||||
const { data } = options
|
||||
if (
|
||||
data &&
|
||||
!isFunction(data) &&
|
||||
softAssertCompatEnabled(DeprecationTypes.OPTIONS_DATA_FN)
|
||||
softAssertCompatEnabled(DeprecationTypes.OPTIONS_DATA_FN, null)
|
||||
) {
|
||||
options.data = () => data
|
||||
}
|
||||
@ -130,7 +130,8 @@ export function createCompatVue(
|
||||
|
||||
// copy prototype augmentations as config.globalProperties
|
||||
const isPrototypeEnabled = isCompatEnabled(
|
||||
DeprecationTypes.GLOBAL_PROTOTYPE
|
||||
DeprecationTypes.GLOBAL_PROTOTYPE,
|
||||
null
|
||||
)
|
||||
let hasPrototypeAugmentations = false
|
||||
for (const key in Ctor.prototype) {
|
||||
@ -142,7 +143,7 @@ export function createCompatVue(
|
||||
}
|
||||
}
|
||||
if (__DEV__ && hasPrototypeAugmentations) {
|
||||
warnDeprecation(DeprecationTypes.GLOBAL_PROTOTYPE)
|
||||
warnDeprecation(DeprecationTypes.GLOBAL_PROTOTYPE, null)
|
||||
}
|
||||
|
||||
const vm = app._createRoot!(options)
|
||||
@ -158,7 +159,7 @@ export function createCompatVue(
|
||||
Vue.nextTick = nextTick
|
||||
|
||||
Vue.extend = ((options: ComponentOptions = {}) => {
|
||||
assertCompatEnabled(DeprecationTypes.GLOBAL_EXTEND)
|
||||
assertCompatEnabled(DeprecationTypes.GLOBAL_EXTEND, null)
|
||||
|
||||
function SubVue(inlineOptions?: ComponentOptions) {
|
||||
if (!inlineOptions) {
|
||||
@ -180,17 +181,17 @@ export function createCompatVue(
|
||||
}) as any
|
||||
|
||||
Vue.set = (target, key, value) => {
|
||||
assertCompatEnabled(DeprecationTypes.GLOBAL_SET)
|
||||
assertCompatEnabled(DeprecationTypes.GLOBAL_SET, null)
|
||||
target[key] = value
|
||||
}
|
||||
|
||||
Vue.delete = (target, key) => {
|
||||
assertCompatEnabled(DeprecationTypes.GLOBAL_DELETE)
|
||||
assertCompatEnabled(DeprecationTypes.GLOBAL_DELETE, null)
|
||||
delete target[key]
|
||||
}
|
||||
|
||||
Vue.observable = (target: any) => {
|
||||
assertCompatEnabled(DeprecationTypes.GLOBAL_OBSERVABLE)
|
||||
assertCompatEnabled(DeprecationTypes.GLOBAL_OBSERVABLE, null)
|
||||
return reactive(target)
|
||||
}
|
||||
|
||||
@ -320,7 +321,7 @@ export function installCompatMount(
|
||||
for (let i = 0; i < container.attributes.length; i++) {
|
||||
const attr = container.attributes[i]
|
||||
if (attr.name !== 'v-cloak' && /^(v-|:|@)/.test(attr.name)) {
|
||||
warnDeprecation(DeprecationTypes.GLOBAL_MOUNT_CONTAINER)
|
||||
warnDeprecation(DeprecationTypes.GLOBAL_MOUNT_CONTAINER, null)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -52,14 +52,14 @@ export function installLegacyConfigTraps(config: AppConfig) {
|
||||
},
|
||||
set(newVal) {
|
||||
if (!isCopyingConfig) {
|
||||
warnDeprecation(legacyConfigOptions[key])
|
||||
warnDeprecation(legacyConfigOptions[key], null)
|
||||
}
|
||||
val = newVal
|
||||
|
||||
// compat for runtime ignoredElements -> isCustomElement
|
||||
if (
|
||||
key === 'ignoredElements' &&
|
||||
isCompatEnabled(DeprecationTypes.CONFIG_IGNORED_ELEMENTS) &&
|
||||
isCompatEnabled(DeprecationTypes.CONFIG_IGNORED_ELEMENTS, null) &&
|
||||
!isRuntimeOnly() &&
|
||||
isArray(newVal)
|
||||
) {
|
||||
|
@ -1,11 +1,12 @@
|
||||
import { extend, NOOP } from '@vue/shared'
|
||||
import { PublicPropertiesMap } from '../componentPublicInstance'
|
||||
import { getCompatChildren } from './instanceChildren'
|
||||
import { assertCompatEnabled } from './compatConfig'
|
||||
import { assertCompatEnabled, isCompatEnabled } from './compatConfig'
|
||||
import { DeprecationTypes, warnDeprecation } from './deprecations'
|
||||
import { off, on, once } from './instanceEventEmitter'
|
||||
import { getCompatListeners } from './instanceListeners'
|
||||
import { shallowReadonly } from '@vue/reactivity'
|
||||
import { legacySlotProxyHandlers } from './component'
|
||||
|
||||
export function installCompatInstanceProperties(map: PublicPropertiesMap) {
|
||||
const set = (target: any, key: any, val: any) => {
|
||||
@ -17,37 +18,48 @@ export function installCompatInstanceProperties(map: PublicPropertiesMap) {
|
||||
}
|
||||
|
||||
extend(map, {
|
||||
$set: () => {
|
||||
assertCompatEnabled(DeprecationTypes.INSTANCE_SET)
|
||||
$set: i => {
|
||||
assertCompatEnabled(DeprecationTypes.INSTANCE_SET, i)
|
||||
return set
|
||||
},
|
||||
|
||||
$delete: () => {
|
||||
assertCompatEnabled(DeprecationTypes.INSTANCE_DELETE)
|
||||
$delete: i => {
|
||||
assertCompatEnabled(DeprecationTypes.INSTANCE_DELETE, i)
|
||||
return del
|
||||
},
|
||||
|
||||
$mount: i => {
|
||||
assertCompatEnabled(DeprecationTypes.GLOBAL_MOUNT)
|
||||
assertCompatEnabled(
|
||||
DeprecationTypes.GLOBAL_MOUNT,
|
||||
null /* this warning is global */
|
||||
)
|
||||
// root mount override from ./global.ts in installCompatMount
|
||||
return i.ctx._compat_mount || NOOP
|
||||
},
|
||||
|
||||
$destroy: i => {
|
||||
assertCompatEnabled(DeprecationTypes.INSTANCE_DESTROY)
|
||||
assertCompatEnabled(DeprecationTypes.INSTANCE_DESTROY, i)
|
||||
// root destroy override from ./global.ts in installCompatMount
|
||||
return i.ctx._compat_destroy || NOOP
|
||||
},
|
||||
|
||||
// overrides existing accessor
|
||||
$slots: i => {
|
||||
if (isCompatEnabled(DeprecationTypes.RENDER_FUNCTION, i)) {
|
||||
return new Proxy(i.slots, legacySlotProxyHandlers)
|
||||
}
|
||||
return i.slots
|
||||
},
|
||||
|
||||
$scopedSlots: i => {
|
||||
assertCompatEnabled(DeprecationTypes.INSTANCE_SCOPED_SLOTS)
|
||||
assertCompatEnabled(DeprecationTypes.INSTANCE_SCOPED_SLOTS, i)
|
||||
return __DEV__ ? shallowReadonly(i.slots) : i.slots
|
||||
},
|
||||
|
||||
// overrides existing accessor
|
||||
$attrs: i => {
|
||||
if (__DEV__ && i.type.inheritAttrs === false) {
|
||||
warnDeprecation(DeprecationTypes.INSTANCE_ATTRS_CLASS_STYLE)
|
||||
warnDeprecation(DeprecationTypes.INSTANCE_ATTRS_CLASS_STYLE, i)
|
||||
}
|
||||
return __DEV__ ? shallowReadonly(i.attrs) : i.attrs
|
||||
},
|
||||
|
@ -8,7 +8,7 @@ import { DeprecationTypes } from './deprecations'
|
||||
export function getCompatChildren(
|
||||
instance: ComponentInternalInstance
|
||||
): ComponentPublicInstance[] {
|
||||
assertCompatEnabled(DeprecationTypes.INSTANCE_CHILDREN)
|
||||
assertCompatEnabled(DeprecationTypes.INSTANCE_CHILDREN, instance)
|
||||
const root = instance.subTree
|
||||
const children: ComponentPublicInstance[] = []
|
||||
if (root) {
|
||||
|
@ -32,9 +32,9 @@ export function on(
|
||||
event.forEach(e => on(instance, e, fn))
|
||||
} else {
|
||||
if (event.startsWith('hook:')) {
|
||||
assertCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS)
|
||||
assertCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
|
||||
} else {
|
||||
assertCompatEnabled(DeprecationTypes.INSTANCE_EVENT_EMITTER)
|
||||
assertCompatEnabled(DeprecationTypes.INSTANCE_EVENT_EMITTER, instance)
|
||||
}
|
||||
const events = getRegistry(instance)
|
||||
;(events[event] || (events[event] = [])).push(fn)
|
||||
@ -61,7 +61,7 @@ export function off(
|
||||
event?: string,
|
||||
fn?: Function
|
||||
) {
|
||||
assertCompatEnabled(DeprecationTypes.INSTANCE_EVENT_EMITTER)
|
||||
assertCompatEnabled(DeprecationTypes.INSTANCE_EVENT_EMITTER, instance)
|
||||
const vm = instance.proxy
|
||||
// all
|
||||
if (!arguments.length) {
|
||||
|
@ -4,7 +4,7 @@ import { assertCompatEnabled } from './compatConfig'
|
||||
import { DeprecationTypes } from './deprecations'
|
||||
|
||||
export function getCompatListeners(instance: ComponentInternalInstance) {
|
||||
assertCompatEnabled(DeprecationTypes.INSTANCE_LISTENERS)
|
||||
assertCompatEnabled(DeprecationTypes.INSTANCE_LISTENERS, instance)
|
||||
|
||||
const listeners: Record<string, Function | Function[]> = {}
|
||||
const rawProps = instance.vnode.props
|
||||
|
@ -5,7 +5,7 @@ export function createPropsDefaultThis(propKey: string) {
|
||||
{},
|
||||
{
|
||||
get() {
|
||||
warnDeprecation(DeprecationTypes.PROPS_DEFAULT_THIS, propKey)
|
||||
warnDeprecation(DeprecationTypes.PROPS_DEFAULT_THIS, null, propKey)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@ -1,8 +1,20 @@
|
||||
import { isArray, isObject } from '@vue/shared'
|
||||
import {
|
||||
extend,
|
||||
isArray,
|
||||
isObject,
|
||||
ShapeFlags,
|
||||
toHandlerKey
|
||||
} from '@vue/shared'
|
||||
import { Component, Data } from '../component'
|
||||
import { DirectiveArguments, withDirectives } from '../directives'
|
||||
import {
|
||||
resolveDirective,
|
||||
resolveDynamicComponent
|
||||
} from '../helpers/resolveAssets'
|
||||
import {
|
||||
createVNode,
|
||||
isVNode,
|
||||
normalizeChildren,
|
||||
VNode,
|
||||
VNodeArrayChildren,
|
||||
VNodeProps
|
||||
@ -23,6 +35,8 @@ interface LegacyVNodeProps {
|
||||
nativeOn?: Record<string, Function | Function[]>
|
||||
directives?: LegacyVNodeDirective[]
|
||||
|
||||
// component only
|
||||
props?: Record<string, unknown>
|
||||
slot?: string
|
||||
scopedSlots?: Record<string, Function>
|
||||
}
|
||||
@ -56,6 +70,9 @@ export function compatH(
|
||||
propsOrChildren?: any,
|
||||
children?: any
|
||||
): VNode {
|
||||
// to support v2 string component name lookup
|
||||
type = resolveDynamicComponent(type)
|
||||
|
||||
const l = arguments.length
|
||||
if (l === 2) {
|
||||
if (isObject(propsOrChildren) && !isArray(propsOrChildren)) {
|
||||
@ -64,10 +81,12 @@ export function compatH(
|
||||
return convertLegacySlots(createVNode(type, null, [propsOrChildren]))
|
||||
}
|
||||
// props without children
|
||||
return convertLegacyDirectives(
|
||||
return convertLegacySlots(
|
||||
convertLegacyDirectives(
|
||||
createVNode(type, convertLegacyProps(propsOrChildren)),
|
||||
propsOrChildren
|
||||
)
|
||||
)
|
||||
} else {
|
||||
// omit props
|
||||
return convertLegacySlots(createVNode(type, null, propsOrChildren))
|
||||
@ -87,17 +106,87 @@ export function compatH(
|
||||
}
|
||||
}
|
||||
|
||||
function convertLegacyProps(props: LegacyVNodeProps): Data & VNodeProps {
|
||||
// TODO
|
||||
return props as any
|
||||
function convertLegacyProps(legacyProps?: LegacyVNodeProps): Data & VNodeProps {
|
||||
const converted: Data & VNodeProps = {}
|
||||
|
||||
for (const key in legacyProps) {
|
||||
if (key === 'attrs' || key === 'domProps' || key === 'props') {
|
||||
extend(converted, legacyProps[key])
|
||||
} else if (key === 'on' || key === 'nativeOn') {
|
||||
const listeners = legacyProps[key]
|
||||
for (const event in listeners) {
|
||||
const handlerKey = toHandlerKey(event)
|
||||
const existing = converted[handlerKey]
|
||||
const incoming = listeners[event]
|
||||
if (existing !== incoming) {
|
||||
converted[handlerKey] = existing
|
||||
? [].concat(existing as any, incoming as any)
|
||||
: incoming
|
||||
}
|
||||
}
|
||||
} else {
|
||||
converted[key] = legacyProps[key as keyof LegacyVNodeProps]
|
||||
}
|
||||
}
|
||||
|
||||
return converted
|
||||
}
|
||||
|
||||
function convertLegacyDirectives(vnode: VNode, props: LegacyVNodeProps): VNode {
|
||||
// TODO
|
||||
function convertLegacyDirectives(
|
||||
vnode: VNode,
|
||||
props?: LegacyVNodeProps
|
||||
): VNode {
|
||||
if (props && props.directives) {
|
||||
return withDirectives(
|
||||
vnode,
|
||||
props.directives.map(({ name, value, arg, modifiers }) => {
|
||||
return [
|
||||
resolveDirective(name)!,
|
||||
value,
|
||||
arg,
|
||||
modifiers
|
||||
] as DirectiveArguments[number]
|
||||
})
|
||||
)
|
||||
}
|
||||
return vnode
|
||||
}
|
||||
|
||||
function convertLegacySlots(vnode: VNode): VNode {
|
||||
// TODO
|
||||
const { props, children } = vnode
|
||||
|
||||
let slots: Record<string, any> | undefined
|
||||
|
||||
if (vnode.shapeFlag & ShapeFlags.COMPONENT && isArray(children)) {
|
||||
slots = {}
|
||||
// check "slot" property on vnodes and turn them into v3 function slots
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
const child = children[i]
|
||||
const slotName =
|
||||
(isVNode(child) && child.props && child.props.slot) || 'default'
|
||||
;(slots[slotName] || (slots[slotName] = [] as any[])).push(child)
|
||||
}
|
||||
if (slots) {
|
||||
for (const key in slots) {
|
||||
const slotChildren = slots[key]
|
||||
slots[key] = () => slotChildren
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const scopedSlots = props && props.scopedSlots
|
||||
if (scopedSlots) {
|
||||
delete props!.scopedSlots
|
||||
if (slots) {
|
||||
extend(slots, scopedSlots)
|
||||
} else {
|
||||
slots = scopedSlots
|
||||
}
|
||||
}
|
||||
|
||||
if (slots) {
|
||||
normalizeChildren(vnode, slots)
|
||||
}
|
||||
|
||||
return vnode
|
||||
}
|
||||
|
@ -54,6 +54,9 @@ import { CompilerOptions } from '@vue/compiler-core'
|
||||
import { markAttrsAccessed } from './componentRenderUtils'
|
||||
import { currentRenderingInstance } from './componentRenderContext'
|
||||
import { startMeasure, endMeasure } from './profiling'
|
||||
import { isCompatEnabled } from './compat/compatConfig'
|
||||
import { DeprecationTypes, warnDeprecation } from './compat/deprecations'
|
||||
import { compatH } from './compat/renderFn'
|
||||
|
||||
export type Data = Record<string, unknown>
|
||||
|
||||
@ -681,6 +684,18 @@ export function finishComponentSetup(
|
||||
) {
|
||||
const Component = instance.type as ComponentOptions
|
||||
|
||||
if (
|
||||
__COMPAT__ &&
|
||||
Component.render &&
|
||||
isCompatEnabled(DeprecationTypes.RENDER_FUNCTION)
|
||||
) {
|
||||
warnDeprecation(DeprecationTypes.RENDER_FUNCTION)
|
||||
const originalRender = Component.render
|
||||
Component.render = function compatRender() {
|
||||
return originalRender.call(this, compatH)
|
||||
}
|
||||
}
|
||||
|
||||
// template / render function normalization
|
||||
if (__NODE_JS__ && isSSR) {
|
||||
// 1. the render function may already exist, returned by `setup`
|
||||
|
@ -790,13 +790,13 @@ export function applyOptions(
|
||||
if (__COMPAT__) {
|
||||
if (
|
||||
beforeDestroy &&
|
||||
softAssertCompatEnabled(DeprecationTypes.OPTIONS_BEFORE_DESTROY)
|
||||
softAssertCompatEnabled(DeprecationTypes.OPTIONS_BEFORE_DESTROY, instance)
|
||||
) {
|
||||
onBeforeUnmount(beforeDestroy.bind(publicThis))
|
||||
}
|
||||
if (
|
||||
destroyed &&
|
||||
softAssertCompatEnabled(DeprecationTypes.OPTIONS_DESTROYED)
|
||||
softAssertCompatEnabled(DeprecationTypes.OPTIONS_DESTROYED, instance)
|
||||
) {
|
||||
onUnmounted(destroyed.bind(publicThis))
|
||||
}
|
||||
@ -930,8 +930,11 @@ function resolveData(
|
||||
instance.data = reactive(data)
|
||||
} else {
|
||||
// existing data: this is a mixin or extends.
|
||||
if (__COMPAT__ && isCompatEnabled(DeprecationTypes.OPTIONS_DATA_MERGE)) {
|
||||
deepMergeData(instance.data, data)
|
||||
if (
|
||||
__COMPAT__ &&
|
||||
isCompatEnabled(DeprecationTypes.OPTIONS_DATA_MERGE, instance)
|
||||
) {
|
||||
deepMergeData(instance.data, data, instance)
|
||||
} else {
|
||||
extend(instance.data, data)
|
||||
}
|
||||
|
@ -127,7 +127,7 @@ export function invokeDirectiveHook(
|
||||
}
|
||||
let hook = binding.dir[name] as DirectiveHook | DirectiveHook[] | undefined
|
||||
if (__COMPAT__ && !hook) {
|
||||
hook = mapCompatDirectiveHook(name, binding.dir)
|
||||
hook = mapCompatDirectiveHook(name, binding.dir, instance)
|
||||
}
|
||||
if (hook) {
|
||||
callWithAsyncErrorHandling(hook, instance, ErrorCodes.DIRECTIVE_HOOK, [
|
||||
|
@ -361,7 +361,7 @@ function _createVNode(
|
||||
|
||||
// 2.x async/functional component compat
|
||||
if (__COMPAT__) {
|
||||
type = convertLegacyComponent(type)
|
||||
type = convertLegacyComponent(type, currentRenderingInstance)
|
||||
}
|
||||
|
||||
// class & style normalization.
|
||||
@ -677,7 +677,7 @@ export function mergeProps(...args: (Data & VNodeProps)[]) {
|
||||
const incoming = toMerge[key]
|
||||
if (existing !== incoming) {
|
||||
ret[key] = existing
|
||||
? [].concat(existing as any, toMerge[key] as any)
|
||||
? [].concat(existing as any, incoming as any)
|
||||
: incoming
|
||||
}
|
||||
} else if (key !== '') {
|
||||
|
Loading…
Reference in New Issue
Block a user