mirror of https://github.com/vuejs/core.git
fix(compat): fix $options mutation + adjust private API initialization
close #10626 close #10636
This commit is contained in:
parent
04af9504a7
commit
d58d133b1c
|
@ -15,6 +15,7 @@ import {
|
||||||
DeprecationTypes,
|
DeprecationTypes,
|
||||||
assertCompatEnabled,
|
assertCompatEnabled,
|
||||||
isCompatEnabled,
|
isCompatEnabled,
|
||||||
|
warnDeprecation,
|
||||||
} from './compatConfig'
|
} from './compatConfig'
|
||||||
import { off, on, once } from './instanceEventEmitter'
|
import { off, on, once } from './instanceEventEmitter'
|
||||||
import { getCompatListeners } from './instanceListeners'
|
import { getCompatListeners } from './instanceListeners'
|
||||||
|
@ -121,50 +122,77 @@ export function installCompatInstanceProperties(map: PublicPropertiesMap) {
|
||||||
|
|
||||||
$children: getCompatChildren,
|
$children: getCompatChildren,
|
||||||
$listeners: getCompatListeners,
|
$listeners: getCompatListeners,
|
||||||
|
|
||||||
|
// inject additional properties into $options for compat
|
||||||
|
// e.g. vuex needs this.$options.parent
|
||||||
|
$options: i => {
|
||||||
|
if (!isCompatEnabled(DeprecationTypes.PRIVATE_APIS, i)) {
|
||||||
|
return resolveMergedOptions(i)
|
||||||
|
}
|
||||||
|
if (i.resolvedOptions) {
|
||||||
|
return i.resolvedOptions
|
||||||
|
}
|
||||||
|
const res = (i.resolvedOptions = extend({}, resolveMergedOptions(i)))
|
||||||
|
Object.defineProperties(res, {
|
||||||
|
parent: {
|
||||||
|
get() {
|
||||||
|
warnDeprecation(DeprecationTypes.PRIVATE_APIS, i, '$options.parent')
|
||||||
|
return i.proxy!.$parent
|
||||||
|
},
|
||||||
|
},
|
||||||
|
propsData: {
|
||||||
|
get() {
|
||||||
|
warnDeprecation(
|
||||||
|
DeprecationTypes.PRIVATE_APIS,
|
||||||
|
i,
|
||||||
|
'$options.propsData',
|
||||||
|
)
|
||||||
|
return i.vnode.props
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return res
|
||||||
|
},
|
||||||
} as PublicPropertiesMap)
|
} as PublicPropertiesMap)
|
||||||
|
|
||||||
/* istanbul ignore if */
|
const privateAPIs = {
|
||||||
if (isCompatEnabled(DeprecationTypes.PRIVATE_APIS, null)) {
|
// needed by many libs / render fns
|
||||||
extend(map, {
|
$vnode: i => i.vnode,
|
||||||
// needed by many libs / render fns
|
|
||||||
$vnode: i => i.vnode,
|
|
||||||
|
|
||||||
// inject additional properties into $options for compat
|
// some private properties that are likely accessed...
|
||||||
// e.g. vuex needs this.$options.parent
|
_self: i => i.proxy,
|
||||||
$options: i => {
|
_uid: i => i.uid,
|
||||||
const res = extend({}, resolveMergedOptions(i))
|
_data: i => i.data,
|
||||||
res.parent = i.proxy!.$parent
|
_isMounted: i => i.isMounted,
|
||||||
res.propsData = i.vnode.props
|
_isDestroyed: i => i.isUnmounted,
|
||||||
return res
|
|
||||||
},
|
|
||||||
|
|
||||||
// some private properties that are likely accessed...
|
// v2 render helpers
|
||||||
_self: i => i.proxy,
|
$createElement: () => compatH,
|
||||||
_uid: i => i.uid,
|
_c: () => compatH,
|
||||||
_data: i => i.data,
|
_o: () => legacyMarkOnce,
|
||||||
_isMounted: i => i.isMounted,
|
_n: () => looseToNumber,
|
||||||
_isDestroyed: i => i.isUnmounted,
|
_s: () => toDisplayString,
|
||||||
|
_l: () => renderList,
|
||||||
|
_t: i => legacyRenderSlot.bind(null, i),
|
||||||
|
_q: () => looseEqual,
|
||||||
|
_i: () => looseIndexOf,
|
||||||
|
_m: i => legacyRenderStatic.bind(null, i),
|
||||||
|
_f: () => resolveFilter,
|
||||||
|
_k: i => legacyCheckKeyCodes.bind(null, i),
|
||||||
|
_b: () => legacyBindObjectProps,
|
||||||
|
_v: () => createTextVNode,
|
||||||
|
_e: () => createCommentVNode,
|
||||||
|
_u: () => legacyresolveScopedSlots,
|
||||||
|
_g: () => legacyBindObjectListeners,
|
||||||
|
_d: () => legacyBindDynamicKeys,
|
||||||
|
_p: () => legacyPrependModifier,
|
||||||
|
} as PublicPropertiesMap
|
||||||
|
|
||||||
// v2 render helpers
|
for (const key in privateAPIs) {
|
||||||
$createElement: () => compatH,
|
map[key] = i => {
|
||||||
_c: () => compatH,
|
if (isCompatEnabled(DeprecationTypes.PRIVATE_APIS, i)) {
|
||||||
_o: () => legacyMarkOnce,
|
return privateAPIs[key](i)
|
||||||
_n: () => looseToNumber,
|
}
|
||||||
_s: () => toDisplayString,
|
}
|
||||||
_l: () => renderList,
|
|
||||||
_t: i => legacyRenderSlot.bind(null, i),
|
|
||||||
_q: () => looseEqual,
|
|
||||||
_i: () => looseIndexOf,
|
|
||||||
_m: i => legacyRenderStatic.bind(null, i),
|
|
||||||
_f: () => resolveFilter,
|
|
||||||
_k: i => legacyCheckKeyCodes.bind(null, i),
|
|
||||||
_b: () => legacyBindObjectProps,
|
|
||||||
_v: () => createTextVNode,
|
|
||||||
_e: () => createCommentVNode,
|
|
||||||
_u: () => legacyresolveScopedSlots,
|
|
||||||
_g: () => legacyBindObjectListeners,
|
|
||||||
_d: () => legacyBindDynamicKeys,
|
|
||||||
_p: () => legacyPrependModifier,
|
|
||||||
} as PublicPropertiesMap)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,7 @@ import { type Directive, validateDirectiveName } from './directives'
|
||||||
import {
|
import {
|
||||||
type ComponentOptions,
|
type ComponentOptions,
|
||||||
type ComputedOptions,
|
type ComputedOptions,
|
||||||
|
type MergedComponentOptions,
|
||||||
type MethodOptions,
|
type MethodOptions,
|
||||||
applyOptions,
|
applyOptions,
|
||||||
resolveMergedOptions,
|
resolveMergedOptions,
|
||||||
|
@ -524,6 +525,12 @@ export interface ComponentInternalInstance {
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
getCssVars?: () => Record<string, string>
|
getCssVars?: () => Record<string, string>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* v2 compat only, for caching mutated $options
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
resolvedOptions?: MergedComponentOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
const emptyAppContext = createAppContext()
|
const emptyAppContext = createAppContext()
|
||||||
|
|
|
@ -14,6 +14,7 @@ beforeEach(() => {
|
||||||
Vue.configureCompat({
|
Vue.configureCompat({
|
||||||
MODE: 2,
|
MODE: 2,
|
||||||
GLOBAL_MOUNT: 'suppress-warning',
|
GLOBAL_MOUNT: 'suppress-warning',
|
||||||
|
PRIVATE_APIS: 'suppress-warning',
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -331,3 +332,43 @@ test('INSTANCE_ATTR_CLASS_STYLE', () => {
|
||||||
)('Anonymous'),
|
)('Anonymous'),
|
||||||
).toHaveBeenWarned()
|
).toHaveBeenWarned()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('$options mutation', () => {
|
||||||
|
const Comp = {
|
||||||
|
props: ['id'],
|
||||||
|
template: '<div/>',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
foo: '',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created(this: any) {
|
||||||
|
expect(this.$options.parent).toBeDefined()
|
||||||
|
expect(this.$options.test).toBeUndefined()
|
||||||
|
this.$options.test = this.id
|
||||||
|
expect(this.$options.test).toBe(this.id)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
new Vue({
|
||||||
|
template: `<div><Comp id="1"/><Comp id="2"/></div>`,
|
||||||
|
components: { Comp },
|
||||||
|
}).$mount()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('other private APIs', () => {
|
||||||
|
new Vue({
|
||||||
|
created() {
|
||||||
|
expect(this.$createElement).toBeTruthy()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
new Vue({
|
||||||
|
compatConfig: {
|
||||||
|
PRIVATE_APIS: false,
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
expect(this.$createElement).toBeUndefined()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
Loading…
Reference in New Issue