wip: more compat tweaks

This commit is contained in:
Evan You 2021-04-22 14:59:54 -04:00
parent 98bc9a26e9
commit 7e0224aa8c
9 changed files with 86 additions and 14 deletions

View File

@ -6,7 +6,7 @@ import {
createApp, createApp,
shallowReadonly shallowReadonly
} from '@vue/runtime-test' } from '@vue/runtime-test'
import { ComponentInternalInstance } from '../src/component' import { ComponentInternalInstance, ComponentOptions } from '../src/component'
describe('component: proxy', () => { describe('component: proxy', () => {
test('data', () => { test('data', () => {
@ -93,7 +93,9 @@ describe('component: proxy', () => {
expect(instanceProxy.$root).toBe(instance!.root.proxy) expect(instanceProxy.$root).toBe(instance!.root.proxy)
expect(instanceProxy.$emit).toBe(instance!.emit) expect(instanceProxy.$emit).toBe(instance!.emit)
expect(instanceProxy.$el).toBe(instance!.vnode.el) expect(instanceProxy.$el).toBe(instance!.vnode.el)
expect(instanceProxy.$options).toBe(instance!.type) expect(instanceProxy.$options).toBe(
(instance!.type as ComponentOptions).__merged
)
expect(() => (instanceProxy.$data = {})).toThrow(TypeError) expect(() => (instanceProxy.$data = {})).toThrow(TypeError)
expect(`Attempting to mutate public property "$data"`).toHaveBeenWarned() expect(`Attempting to mutate public property "$data"`).toHaveBeenWarned()

View File

@ -26,13 +26,18 @@ export function convertLegacyComponent(
return comp return comp
} }
// 2.x constructor
if (isFunction(comp) && comp.cid) {
comp = comp.options
}
// 2.x async component // 2.x async component
// since after disabling this, plain functions are still valid usage, do not
// use softAssert here.
if ( if (
isFunction(comp) && isFunction(comp) &&
checkCompatEnabled(DeprecationTypes.COMPONENT_ASYNC, instance, comp) checkCompatEnabled(DeprecationTypes.COMPONENT_ASYNC, instance, comp)
) { ) {
// since after disabling this, plain functions are still valid usage, do not
// use softAssert here.
return convertLegacyAsyncComponent(comp) return convertLegacyAsyncComponent(comp)
} }

View File

@ -94,6 +94,14 @@ export type CompatVue = Pick<App, 'version' | 'component' | 'directive'> & {
* @deprecated filters have been removed from Vue 3. * @deprecated filters have been removed from Vue 3.
*/ */
filter(name: string, arg: any): null filter(name: string, arg: any): null
/**
* @internal
*/
cid: number
/**
* @internal
*/
options: ComponentOptions
configureCompat: typeof configureCompat configureCompat: typeof configureCompat
} }
@ -109,8 +117,6 @@ export function createCompatVue(
} as any } as any
const singletonApp = createApp({}) const singletonApp = createApp({})
// @ts-ignore
singletonApp.prototype = singletonApp.config.globalProperties
function createCompatApp(options: ComponentOptions = {}, Ctor: any) { function createCompatApp(options: ComponentOptions = {}, Ctor: any) {
assertCompatEnabled(DeprecationTypes.GLOBAL_MOUNT, null) assertCompatEnabled(DeprecationTypes.GLOBAL_MOUNT, null)
@ -174,18 +180,26 @@ export function createCompatVue(
Vue.version = __VERSION__ Vue.version = __VERSION__
Vue.config = singletonApp.config Vue.config = singletonApp.config
Vue.nextTick = nextTick Vue.nextTick = nextTick
Vue.options = { _base: Vue }
Vue.extend = ((options: ComponentOptions = {}) => { let cid = 1
Vue.cid = cid
function extendCtor(this: any, extendOptions: ComponentOptions = {}) {
assertCompatEnabled(DeprecationTypes.GLOBAL_EXTEND, null) assertCompatEnabled(DeprecationTypes.GLOBAL_EXTEND, null)
if (isFunction(extendOptions)) {
extendOptions = extendOptions.options
}
const Super = this
function SubVue(inlineOptions?: ComponentOptions) { function SubVue(inlineOptions?: ComponentOptions) {
if (!inlineOptions) { if (!inlineOptions) {
return createCompatApp(options, SubVue) return createCompatApp(extendOptions, SubVue)
} else { } else {
return createCompatApp( return createCompatApp(
{ {
el: inlineOptions.el, el: inlineOptions.el,
extends: options, extends: extendOptions,
mixins: [inlineOptions] mixins: [inlineOptions]
}, },
SubVue SubVue
@ -194,8 +208,20 @@ export function createCompatVue(
} }
SubVue.prototype = Object.create(Vue.prototype) SubVue.prototype = Object.create(Vue.prototype)
SubVue.prototype.constructor = SubVue SubVue.prototype.constructor = SubVue
SubVue.options = mergeOptions(
extend({}, Super.options) as ComponentOptions,
extendOptions
)
SubVue.options._base = SubVue
SubVue.extend = extendCtor.bind(SubVue)
SubVue.mixin = Super.mixin
SubVue.use = Super.use
SubVue.cid = ++cid
return SubVue return SubVue
}) as any }
Vue.extend = extendCtor.bind(Vue) as any
Vue.set = (target, key, value) => { Vue.set = (target, key, value) => {
assertCompatEnabled(DeprecationTypes.GLOBAL_SET, null) assertCompatEnabled(DeprecationTypes.GLOBAL_SET, null)
@ -213,7 +239,11 @@ export function createCompatVue(
} }
Vue.use = (p, ...options) => { Vue.use = (p, ...options) => {
singletonApp.use(p, ...options) if (p && isFunction(p.install)) {
p.install(Vue as any, ...options)
} else if (isFunction(p)) {
p(Vue as any, ...options)
}
return Vue return Vue
} }

View File

@ -110,8 +110,11 @@ export function installLegacyConfigProperties(config: AppConfig) {
strats.watch = mergeObjectOptions strats.watch = mergeObjectOptions
} }
function mergeHook(to: Function[] | undefined, from: Function | Function[]) { function mergeHook(
return Array.from(new Set([...(to || []), from])) to: Function[] | Function | undefined,
from: Function | Function[]
) {
return Array.from(new Set([...(isArray(to) ? to : to ? [to] : []), from]))
} }
function mergeObjectOptions(to: Object | undefined, from: Object | undefined) { function mergeObjectOptions(to: Object | undefined, from: Object | undefined) {

View File

@ -32,6 +32,7 @@ import {
legacyresolveScopedSlots legacyresolveScopedSlots
} from './renderHelpers' } from './renderHelpers'
import { resolveFilter } from '../helpers/resolveAssets' import { resolveFilter } from '../helpers/resolveAssets'
import { resolveMergedOptions } from '../componentOptions'
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) => {
@ -92,9 +93,18 @@ export function installCompatInstanceProperties(map: PublicPropertiesMap) {
$children: getCompatChildren, $children: getCompatChildren,
$listeners: getCompatListeners, $listeners: getCompatListeners,
// inject parent into $options for compat
$options: i => {
let res = resolveMergedOptions(i)
if (res === i.type) res = i.type.__merged = extend({}, res)
res.parent = i.proxy!.$parent
return res
},
// v2 render helpers // v2 render helpers
$createElement: () => compatH, $createElement: () => compatH,
_self: i => i.proxy, _self: i => i.proxy,
_uid: i => i.uid,
_c: () => compatH, _c: () => compatH,
_o: () => legacyMarkOnce, _o: () => legacyMarkOnce,
_n: () => toNumber, _n: () => toNumber,

View File

@ -3,6 +3,7 @@ import {
hyphenate, hyphenate,
isArray, isArray,
isObject, isObject,
isString,
makeMap, makeMap,
normalizeClass, normalizeClass,
normalizeStyle, normalizeStyle,
@ -304,12 +305,17 @@ export function defineLegacyVNodeProperties(vnode: VNode) {
if ( if (
isCompatEnabled(DeprecationTypes.RENDER_FUNCTION, currentRenderingInstance) isCompatEnabled(DeprecationTypes.RENDER_FUNCTION, currentRenderingInstance)
) { ) {
const context = currentRenderingInstance
const getInstance = () => vnode.component && vnode.component.proxy const getInstance = () => vnode.component && vnode.component.proxy
let componentOptions: any let componentOptions: any
Object.defineProperties(vnode, { Object.defineProperties(vnode, {
tag: { get: () => vnode.type },
data: { get: () => vnode.props, set: p => (vnode.props = p) },
elm: { get: () => vnode.el }, elm: { get: () => vnode.el },
componentInstance: { get: getInstance }, componentInstance: { get: getInstance },
child: { get: getInstance }, child: { get: getInstance },
text: { get: () => (isString(vnode.children) ? vnode.children : null) },
context: { get: () => context && context.proxy },
componentOptions: { componentOptions: {
get: () => { get: () => {
if (vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT) { if (vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT) {

View File

@ -509,6 +509,10 @@ export function applyOptions(
deferredProvide: (Data | Function)[] = [], deferredProvide: (Data | Function)[] = [],
asMixin: boolean = false asMixin: boolean = false
) { ) {
if (__COMPAT__ && isFunction(options)) {
options = options.options
}
const { const {
// composition // composition
mixins, mixins,
@ -1005,6 +1009,10 @@ export function mergeOptions(
from: any, from: any,
instance?: ComponentInternalInstance instance?: ComponentInternalInstance
) { ) {
if (__COMPAT__ && isFunction(from)) {
from = from.options
}
const strats = instance && instance.appContext.config.optionMergeStrategies const strats = instance && instance.appContext.config.optionMergeStrategies
const { mixins, extends: extendsOptions } = from const { mixins, extends: extendsOptions } = from
@ -1019,4 +1027,5 @@ export function mergeOptions(
to[key] = from[key] to[key] = from[key]
} }
} }
return to
} }

View File

@ -429,6 +429,9 @@ export function normalizePropsOptions(
let hasExtends = false let hasExtends = false
if (__FEATURE_OPTIONS_API__ && !isFunction(comp)) { if (__FEATURE_OPTIONS_API__ && !isFunction(comp)) {
const extendProps = (raw: ComponentOptions) => { const extendProps = (raw: ComponentOptions) => {
if (__COMPAT__ && isFunction(raw)) {
raw = raw.options
}
hasExtends = true hasExtends = true
const [props, keys] = normalizePropsOptions(raw, appContext, true) const [props, keys] = normalizePropsOptions(raw, appContext, true)
extend(normalized, props) extend(normalized, props)

View File

@ -107,7 +107,11 @@ const normalizeVNodeSlots = (
instance: ComponentInternalInstance, instance: ComponentInternalInstance,
children: VNodeNormalizedChildren children: VNodeNormalizedChildren
) => { ) => {
if (__DEV__ && !isKeepAlive(instance.vnode)) { if (
__DEV__ &&
!isKeepAlive(instance.vnode) &&
!(__COMPAT__ && isCompatEnabled(DeprecationTypes.RENDER_FUNCTION, instance))
) {
warn( warn(
`Non-function value encountered for default slot. ` + `Non-function value encountered for default slot. ` +
`Prefer function slots for better performance.` `Prefer function slots for better performance.`